diff --git a/.classpath b/.classpath deleted file mode 100644 index 04b10efc..00000000 --- a/.classpath +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.github/workflows/ant.yml b/.github/workflows/ant.yml deleted file mode 100644 index d2fcbee3..00000000 --- a/.github/workflows/ant.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Java CI - -on: - push: - branches: - - master - - $default-branch - - $protected-branches - pull_request: - branches: - - master - - $default-branch - workflow_dispatch: - -jobs: - call-workflow: - strategy: - matrix: - josm-revision: ["", "r19067"] - uses: JOSM/JOSMPluginAction/.github/workflows/ant.yml@v3 - with: - java-version: 17 - josm-revision: ${{ matrix.josm-revision }} - plugin-jar-name: 'mapwithai' - perform-revision-tagging: ${{ matrix.josm-revision == 'r19067' && github.repository == 'JOSM/MapWithAI' && github.ref_type == 'branch' && github.ref_name == 'master' && github.event_name != 'schedule' && github.event_name != 'pull_request' }} - secrets: inherit - diff --git a/.github/workflows/reports.yaml b/.github/workflows/reports.yaml deleted file mode 100644 index 007304f0..00000000 --- a/.github/workflows/reports.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: Publish reports - -on: - workflow_run: - workflows: [Java CI] - types: [completed] - -permissions: - checks: write - -jobs: - call-workflow: - uses: JOSM/JOSMPluginAction/.github/workflows/reports.yaml@v3 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 34d58eb7..00000000 --- a/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -/.gradle/ -/.idea/ -/bin/ -/build/ -/javadoc/ -/out/ -/checkstyle-josm-rapid.xml -/findbugs-josm-rapid.xml -/lib/ -.DS_Store diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f8efcd49..2c90f2ed 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,388 +1,11 @@ -image: registry.gitlab.com/josm/docker-library/openjdk:8 +image: alpine:latest -cache: &global_cache - paths: - - .gradle/wrapper - - .gradle/caches - -before_script: - - export GRADLE_USER_HOME=`pwd`/.gradle - -############################# -# Various additional Checks # -############################# -include: - - template: Code-Quality.gitlab-ci.yml - - template: SAST.gitlab-ci.yml - - template: Dependency-Scanning.gitlab-ci.yml - - template: License-Scanning.gitlab-ci.yml -# - template: Container-Scanning.gitlab-ci.yml -# - template: DAST.gitlab-ci.yml - -stages: - - build - - test - - deploy - - release - -sast: - variables: {} - variables: - GRADLE_PATH: "./gradlew" - FAIL_NEVER: 1 - SAST_EXCLUDED_PATHS: ".gradle" -# CI_DEBUG_TRACE: "true" - -variables: - GIT_SUBMODULE_STRATEGY: recursive - PLUGIN_NAME: "MapWithAI" - PLUGIN_JAR_BASE_NAME: "mapwithai" - -############### -# Build stage # -############### - -assemble: - stage: build - script: - - ./gradlew assemble --stacktrace - artifacts: - paths: - - build/ - expire_in: 1 day - interruptible: true - -assemble with java 11: - stage: build - image: registry.gitlab.com/josm/docker-library/openjdk:11 - script: - - ./gradlew assemble --stacktrace - artifacts: - paths: - - build/ - expire_in: 1 day - interruptible: true - -assemble with java 17: - stage: build - image: registry.gitlab.com/josm/docker-library/openjdk:17 - script: - - ./gradlew assemble --stacktrace - artifacts: - paths: - - build/ - expire_in: 1 day - allow_failure: true - interruptible: true - -code_navigation: - stage: build - script: - - apk add --update curl bash - - curl -fLo coursier https://git.io/coursier-cli - - chmod +x coursier - - ./coursier launch com.sourcegraph:lsif-java_2.13:0.7.2 -- index --build-tool gradle - artifacts: - reports: - lsif: dump.lsif - rules: - - if: '$CI_PIPELINE_SOURCE != "schedule" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - when: always - allow_failure: true - interruptible: true - cache: - <<: *global_cache - policy: pull - -############## -# Test stage # -############## - -build: - stage: test - script: - - ./gradlew build localDist --stacktrace #--info - artifacts: - paths: - - build - expire_in: 1 day - needs: ["assemble"] - dependencies: - - assemble - interruptible: true - -test: - stage: test - script: - - ./gradlew check --stacktrace --continue #--info - - ./gradlew jacocoTestReport - - ./gradlew jacocoTestCoverageVerification - artifacts: - paths: - - build - reports: - junit: build/test-results/**/TEST-*.xml - needs: ["assemble"] - dependencies: - - assemble - interruptible: true - coverage: '/.*Instruction Coverage.*?([0-9.]{1,8}) ?%/' - -coverage: +pages: stage: deploy - needs: ["test"] - dependencies: - - test - image: haynes/jacoco2cobertura:1.0.8 - script: - - python /opt/cover2cover.py build/reports/jacoco/test/jacocoTestReport.xml $CI_PROJECT_DIR/src/main/java/ > build/reports/jacoco/test/coverage.xml - artifacts: - reports: - coverage_report: - coverage_format: cobertura - path: "build/reports/jacoco/test/coverage.xml" - interruptible: true - cache: - <<: *global_cache - policy: pull - -translate: - stage: test script: - - ./gradlew generatePot --stacktrace + - echo 'Nothing to do...' artifacts: paths: - - build - needs: ["assemble"] - cache: - <<: *global_cache - policy: pull - -compile against min JOSM: - stage: test - script: - - ./gradlew compileJava_minJosm --stacktrace - needs: ["assemble"] - dependencies: - - assemble - interruptible: true - allow_failure: true # It should still run against it, but there are some methods if'd around - cache: - <<: *global_cache - policy: pull - -compile against latest JOSM: - stage: test - script: - - ./gradlew compileJava_latestJosm --stacktrace - needs: ["assemble"] - dependencies: - - assemble - interruptible: true - cache: - <<: *global_cache - policy: pull - -build with java 11: - stage: test - image: registry.gitlab.com/josm/docker-library/openjdk:11 - script: - - ./gradlew build --stacktrace - needs: ["assemble with java 11"] - dependencies: - - assemble with java 11 - interruptible: true - cache: - <<: *global_cache - policy: pull - -build with java 17: - stage: test - image: registry.gitlab.com/josm/docker-library/openjdk:17 - script: - - ./gradlew build --stacktrace - allow_failure: true - needs: ["assemble with java 17"] - dependencies: - - assemble with java 17 - interruptible: true - cache: - <<: *global_cache - policy: pull - -################ -# Deploy stage # -################ - -transifex.com: - image: registry.gitlab.com/josm/docker-library/python-transifex:latest - stage: deploy - environment: - name: transifex.com - url: https://www.transifex.com/josm/josm/josm-plugin_$PLUGIN_NAME/ - script: - - TX_TOKEN="$TRANSIFEX_TOKEN" tx push -s --no-interactive - needs: ["translate"] - only: - refs: - - master - variables: - - $TRANSIFEX_TOKEN - cache: - <<: *global_cache - policy: pull - -codecov.io: - image: alpine:3.10 - stage: deploy - environment: - name: codecov.io - url: https://codecov.io/gh/JOSM/$PLUGIN_NAME - before_script: - - apk add --update curl bash - script: - - curl -s https://codecov.io/bash | bash - - curl -s https://codecov.io/bash | bash /dev/stdin -c -F model_and_api - needs: ["build"] + - public only: - refs: - - master - variables: - - $CODECOV_TOKEN - -sonarcloud.io: - image: registry.gitlab.com/josm/docker-library/openjdk:11 - stage: deploy - environment: - name: sonarcloud.io - url: https://sonarcloud.io/dashboard?id=$PLUGIN_NAME - script: - - git fetch --unshallow || echo "Already unshallowed" - - ./gradlew -Dsonar.login=$SONAR_TOKEN sonarqube - needs: ["test"] - dependencies: - - test - only: - refs: - - $CI_DEFAULT_BRANCH - variables: - - $SONAR_TOKEN =~ /[0-9a-z]+/ - -GitLab Maven repo: - stage: deploy - environment: - name: GitLab.com / Maven packages - url: $CI_PROJECT_URL/-/packages - script: - - ./gradlew publishAllPublicationsToGitlabRepository - needs: ["build", "compile against min JOSM", "compile against latest JOSM"] - rules: - - if: '$CI_COMMIT_REF_PROTECTED == "true" && $CI_COMMIT_TAG != null && $CI_PIPELINE_SOURCE != "schedule"' - when: always - cache: - <<: *global_cache - policy: pull - - -################# -# Release stage # -################# - -release: - stage: release - environment: - name: pages branch / dist directory - url: ${CI_PAGES_URL}/${CI_PROJECT_NAME} - script: - - &clone_pages_branch | - echo "$SSH_PRIVATE_DEPLOY_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - git clone --depth 1 --branch pages git@${CI_SERVER_HOST}:${CI_PROJECT_PATH} pages - - ¤t_version | - version=`git describe --always --dirty` - longVersion=`git describe --always --long --dirty` - commitMessage="Release version $longVersion" - - | - #mkdir -pv "pages/public/dist/$version" - #cp -v "build/dist/"* "build/tmp/jar/MANIFEST.MF" "pages/public/dist/$version" - rm -fv "pages/public/dist/latest" - ln -s "./$version" "pages/public/dist/latest" - - &push_pages_branch | - cd pages/ - git config user.name "Deploy with GitLab CI" - git config user.email "${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}@${CI_SERVER_HOST}" - git stage . - git commit -a -m "$commitMessage" - git push origin pages - needs: ["build", "compile against min JOSM", "compile against latest JOSM"] - dependencies: - - build - rules: - - if: '$SSH_PRIVATE_DEPLOY_KEY != null && $CI_COMMIT_REF_PROTECTED == "true" && $CI_COMMIT_TAG != null && $CI_PIPELINE_SOURCE != "schedule"' - when: manual - cache: - <<: *global_cache - policy: pull - -release hotfix: - stage: release - environment: - name: pages branch / dist directory - url: ${CI_PAGES_URL}/${CI_PROJECT_NAME} - script: - - *clone_pages_branch - - *current_version - - | - mkdir -pv "pages/public/dist/$version" - cp -v "build/dist/"* "build/tmp/jar/MANIFEST.MF" "pages/public/dist/$version" - - *push_pages_branch - needs: ["compile against min JOSM", "compile against latest JOSM", "build"] - dependencies: - - build - rules: - - if: '$SSH_PRIVATE_DEPLOY_KEY != null && $CI_COMMIT_REF_PROTECTED == "true" && $CI_COMMIT_TAG != null && $CI_PIPELINE_SOURCE != "schedule" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH' - when: always - -publish update site: - stage: release - environment: - name: pages branch / snapshot update site - url: ${CI_PAGES_URL}/${CI_PROJECT_NAME}/snapshot/${CI_COMMIT_REF_NAME}/update-site - script: - - *clone_pages_branch - - | - commitHash=`git rev-parse HEAD` - commitMessage="Make latest commit from ${CI_COMMIT_REF_NAME} available via JOSM update site ($commitHash)" - - | - rm -vrf "pages/public/snapshot/${CI_COMMIT_REF_NAME}" - mkdir -pv "pages/public/snapshot/${CI_COMMIT_REF_NAME}" - rm -vrf "pages/public/snapshot/libs" - mkdir -pv "pages/public/snapshot/libs" - cp -v "build/snapshot-update-site" "pages/public/snapshot/${CI_COMMIT_REF_NAME}/update-site" - cp -v "build/localDist/list" "pages/public/snapshot/${CI_COMMIT_REF_NAME}/update-site" - sed -i "1s/.*/${PLUGIN_JAR_BASE_NAME}-dev.jar;${CI_PAGES_URL}/${CI_PROJECT_NAME}/snapshot/${CI_COMMIT_REF_NAME}/${PLUGIN_JAR_BASE_NAME}-dev.jar" - cp -v "build/dist/"* "pages/public/snapshot/${CI_COMMIT_REF_NAME}" - cp -v "build/dist/${PLUGIN_JAR_BASE_NAME}.jar" "pages/public/snapshot/${CI_COMMIT_REF_NAME}/${PLUGIN_JAR_BASE_NAME}-dev.jar" - cp -v "build/libs/"*"test-fixture"* "pages/public/snapshot/libs/mapwithai-test-fixture.jar" - - *push_pages_branch - needs: ["compile against min JOSM", "compile against latest JOSM", "build"] - dependencies: - - build - rules: - - if: '$SSH_PRIVATE_DEPLOY_KEY != null && $CI_PIPELINE_SOURCE != "schedule" && $CI_COMMIT_REF_NAME != null' - when: always - -release to Gitlab.com: - stage: release - environment: - name: GitLab.com / Releases - url: $CI_PROJECT_URL/-/releases - script: - - ./gradlew releaseToGitlab - needs: ["GitLab Maven repo"] - rules: - - if: '$SSH_PRIVATE_DEPLOY_KEY != null && $CI_COMMIT_REF_PROTECTED == "true" && $CI_COMMIT_TAG != null && $CI_PIPELINE_SOURCE != "schedule" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH' - when: always - cache: - <<: *global_cache - policy: pull + - pages diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS deleted file mode 100644 index 5aab2e26..00000000 --- a/.gitlab/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @smocktaylor diff --git a/.gitlab/issue_templates/bug.md b/.gitlab/issue_templates/bug.md deleted file mode 100644 index 914d8508..00000000 --- a/.gitlab/issue_templates/bug.md +++ /dev/null @@ -1,40 +0,0 @@ -Summary - -(Summarize the bug encountered concisely) - - -Steps to reproduce - -(How one can reproduce the issue - this is very important) - - -What is the current bug behavior? - -(What actually happens) - - -What is the expected correct behavior? - -(What you should see instead) - -JOSM Status Report -``` -(Replace this with the JOSM Status Report (Help -> Show Status Report)) -``` - -Relevant logs and/or screenshots - -(Paste any relevant logs - please use code blocks (```) to format console output, -logs, and code as it's very hard to read otherwise.) - - -Possible fixes - -(If you can, link to the line of code that might be responsible for the problem) - -/label ~bug ~needs-investigation -/cc @smocktaylor -/assign @smocktaylor -/due in 1 week -/todo - diff --git a/gh-pages/.nojekyll b/.nojekyll similarity index 100% rename from gh-pages/.nojekyll rename to .nojekyll diff --git a/.project b/.project deleted file mode 100644 index d184dbb0..00000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - JOSM-MapWithAI - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index d013a703..00000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,117 +0,0 @@ -# -#Thu Sep 26 10:39:11 MDT 2019 -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled -org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning -org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore -org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore -org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore -org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning -org.eclipse.jdt.core.compiler.annotation.nullable.secondary= -org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore -org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning -org.eclipse.jdt.core.compiler.problem.unusedLocal=warning -org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedLabel=warning -org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled -org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning -org.eclipse.jdt.core.compiler.problem.APILeak=warning -org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info -org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled -org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning -org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error -org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning -org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning -org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled -org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore -org.eclipse.jdt.core.compiler.problem.deprecation=warning -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled -org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning -org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore -org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning -org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull -org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore -org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled -org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore -org.eclipse.jdt.core.compiler.problem.unusedImport=warning -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault -org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable -org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=enabled -org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning -org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore -org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore -org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore -org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled -org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore -org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning -org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning -org.eclipse.jdt.core.compiler.problem.nullReference=warning -org.eclipse.jdt.core.compiler.source=1.8 -org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore -org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore -org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore -org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore -org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning -org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore -org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled -org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning -org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore -org.eclipse.jdt.core.compiler.problem.discouragedReference=warning -org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore -org.eclipse.jdt.core.compiler.problem.deadCode=warning -org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled -org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled -org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning -org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled -org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled -org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error -org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= -org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore -org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning -org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning -org.eclipse.jdt.core.compiler.problem.autoboxing=ignore -org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore -org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning -org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore -org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled -org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled -org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore -org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=error -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled -org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs deleted file mode 100644 index e005f4eb..00000000 --- a/.settings/org.eclipse.jdt.ui.prefs +++ /dev/null @@ -1,61 +0,0 @@ -eclipse.preferences.version=1 -editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true -sp_cleanup.add_default_serial_version_id=true -sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=true -sp_cleanup.add_missing_deprecated_annotations=true -sp_cleanup.add_missing_methods=false -sp_cleanup.add_missing_nls_tags=false -sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=true -sp_cleanup.add_serial_version_id=false -sp_cleanup.always_use_blocks=true -sp_cleanup.always_use_parentheses_in_expressions=false -sp_cleanup.always_use_this_for_non_static_field_access=false -sp_cleanup.always_use_this_for_non_static_method_access=false -sp_cleanup.convert_functional_interfaces=false -sp_cleanup.convert_to_enhanced_for_loop=false -sp_cleanup.correct_indentation=true -sp_cleanup.format_source_code=true -sp_cleanup.format_source_code_changes_only=true -sp_cleanup.insert_inferred_type_arguments=false -sp_cleanup.make_local_variable_final=false -sp_cleanup.make_parameters_final=false -sp_cleanup.make_private_fields_final=true -sp_cleanup.make_type_abstract_if_missing_method=false -sp_cleanup.make_variable_declarations_final=false -sp_cleanup.never_use_blocks=false -sp_cleanup.never_use_parentheses_in_expressions=true -sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=true -sp_cleanup.qualify_static_field_accesses_with_declaring_class=false -sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true -sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true -sp_cleanup.qualify_static_member_accesses_with_declaring_class=false -sp_cleanup.qualify_static_method_accesses_with_declaring_class=false -sp_cleanup.remove_private_constructors=true -sp_cleanup.remove_redundant_modifiers=false -sp_cleanup.remove_redundant_semicolons=true -sp_cleanup.remove_redundant_type_arguments=false -sp_cleanup.remove_trailing_whitespaces=true -sp_cleanup.remove_trailing_whitespaces_all=true -sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=true -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=true -sp_cleanup.remove_unused_local_variables=false -sp_cleanup.remove_unused_private_fields=true -sp_cleanup.remove_unused_private_members=false -sp_cleanup.remove_unused_private_methods=true -sp_cleanup.remove_unused_private_types=true -sp_cleanup.sort_members=false -sp_cleanup.sort_members_all=false -sp_cleanup.use_anonymous_class_creation=false -sp_cleanup.use_blocks=true -sp_cleanup.use_blocks_only_for_return_and_throw=false -sp_cleanup.use_lambda=true -sp_cleanup.use_parentheses_in_expressions=false -sp_cleanup.use_this_for_non_static_field_access=false -sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true -sp_cleanup.use_this_for_non_static_method_access=false -sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d159169d..00000000 --- a/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/README.md b/README.md deleted file mode 100644 index c3f9c84b..00000000 --- a/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# JOSM MapWithAI Plugin (formerly RapiD Plugin) - -[![pipeline status](https://github.com/JOSM/MapWithAI/actions/workflows/ant.yml/badge.svg)](https://github.com/JOSM/MapWithAI/actions/workflows/ant.yml) -[![code coverage](https://gitlab.com/gokaart/JOSM_MapWithAI/badges/master/coverage.svg)](https://codecov.io/github/gokaart/JOSM_MapWithAI?branch=master) -[![license](https://img.shields.io/badge/license-GPLv2-blue.svg?style=flat-square)](./LICENSE) - -This plugin brings MapWithAI information into JOSM. - - -## Installation - -To use this plugin, [install JOSM](https://josm.openstreetmap.de/wiki/Download) and then [in the preferences menu install the **MapWithAI** plugin](https://josm.openstreetmap.de/wiki/Help/Preferences/Plugins#AutomaticinstallationviaPreferencesmenu) - -# How to use the plugin -See the [wiki page](https://josm.openstreetmap.de/wiki/Help/Plugin/MapWithAI). - -## Information -* [RapiD](https://mapwith.ai/rapid) -* [HOT Tasking Manager + RapiD](https://tasks-assisted.hotosm.org/) -* [RapiD source code and country requests](https://github.com/facebookincubator/RapiD) -* [mapwith.ai](https://mapwith.ai/) - -## Contributing - -- The **source code** is hosted on [GitHub](https://github.com/JOSM/MapWithAI). -- **Issues** are managed in [JOSM Trac](https://josm.openstreetmap.de/query?status=assigned&status=needinfo&status=new&status=reopened&component=Plugin+mapwithai&group=component&max=200&col=id&col=summary&col=component&col=status&col=type&col=priority&order=priority&report=17) - - Report a [New Ticket](https://josm.openstreetmap.de/newticket?component=Plugin+mapwithai) -- **Translations** are not currently done. - -## Authors - -- Taylor Smock (taylor.smock) - -## License - -GPLv2 or later (same as JOSM) diff --git a/_redirects b/_redirects new file mode 100644 index 00000000..76146bb1 --- /dev/null +++ b/_redirects @@ -0,0 +1,2 @@ +/JOSM_MapWithAI/dist/v2.0.0-alpha.44/Mapillary.jar https://gitlab.com/smocktaylor/JOSM_MapWithAI/-/package_files/21357258/download 200 +/JOSM_MapWithAI/dist/v2.0.0-alpha.36/Mapillary.jar https://github.com/JOSM/Mapillary/releases/download/v2.0.0-alpha.36/Mapillary.jar 302 diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 706b65c2..00000000 --- a/build.gradle +++ /dev/null @@ -1,312 +0,0 @@ -import groovy.xml.XmlParser - -plugins { - id "com.diffplug.spotless" version "6.25.0" - id "com.github.ben-manes.versions" version "0.51.0" - id "com.github.spotbugs" version "6.0.8" - // id "de.aaschmid.cpd" version "3.3" - id "eclipse" - id "jacoco" - id "java" - id "java-test-fixtures" /* Used for publishing test fixtures package */ - id "maven-publish" - id "net.ltgt.errorprone" version "3.1.0" - id "org.openstreetmap.josm" version "0.8.2" - id "org.sonarqube" version "4.4.1.3373" - id "pmd" -} - -archivesBaseName = "mapwithai" -def gitlabGroup = "gokaart" -def gitlabRepositoryName = "JOSM_MapWithAI" - -repositories { - mavenCentral() - maven { - url "https://josm.openstreetmap.de/nexus/content/repositories/releases/" - } -} - -def versions = [ - awaitility: "4.2.0", - equalsverifier: "3.15.8", - errorprone: "2.26.0", - findsecbugs: "1.13.0", - jacoco: "0.8.10", - jmockit: "1.49.a", - josm: properties.get("plugin.compile.version"), - junit: "5.10.2", - pmd: "6.20.0", - spotbugs: "4.8.3", - wiremock: "2.35.0", -] - -dependencies { - spotbugsPlugins "com.h3xstream.findsecbugs:findsecbugs-plugin:${versions.findsecbugs}" - errorprone("com.google.errorprone:error_prone_core:${versions.errorprone}") - - testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:${versions.junit}") - testFixturesRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${versions.junit}") - testFixturesImplementation("org.junit.vintage:junit-vintage-engine:${versions.junit}") - testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:${versions.junit}") - testFixturesImplementation("org.jmockit:jmockit:${versions.jmockit}") - testFixturesImplementation("com.github.spotbugs:spotbugs-annotations:${versions.spotbugs}") - testFixturesImplementation("org.openstreetmap.josm:josm:${versions.josm}") - testFixturesImplementation("org.openstreetmap.josm:josm-unittest:"){changing=true} - testFixturesImplementation("com.github.tomakehurst:wiremock-jre8:${versions.wiremock}") - testFixturesImplementation("org.awaitility:awaitility:${versions.awaitility}") - testImplementation("nl.jqno.equalsverifier:equalsverifier:${versions.equalsverifier}") -} - -configurations { - testImplementation.extendsFrom testFixturesImplementation - testRuntimeOnly.extendsFrom testFixturesRuntimeOnly - intTestRuntimeOnly.extendsFrom testRuntimeOnly - intTestImplementation.extendsFrom testImplementation -} - -int getJavaVersion() { - // We want to use whatever Java version CI has as default - def ci = project.hasProperty("isCI") or project.hasProperty("CI") or System.getenv("CI") != null - // But we want to override if someone set a specific Java version - def javaVersion = System.getenv("JAVA_VERSION")?.isInteger() ? Integer.valueOf(System.getenv("JAVA_VERSION")) : null - if (javaVersion != null) { - return javaVersion - } - if (ci) { - return Integer.valueOf(JavaVersion.current().getMajorVersion()) - } - return 17 -} - -logger.lifecycle("Using Java " + getJavaVersion()) - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(getJavaVersion())) - } -} - -// Set up Errorprone -tasks.withType(JavaCompile).configureEach { - options.errorprone { - error( - "ClassCanBeStatic", - "DefaultCharset", - "ReferenceEquality", - "WildcardImport" - ) - warn( - "ConstantField", - "FieldCanBeFinal", - "LambdaFunctionalInterface", - "MethodCanBeStatic", - "MultiVariableDeclaration", - "PrivateConstructorForUtilityClass", - "RemoveUnusedImports", - "UngroupedOverloads" - ) - } -} - -rootProject.tasks.named("jar") { - duplicatesStrategy = 'include' -} - - -sourceSets { - test { - java { - srcDirs = ["src/test/unit"] - } - resources { - srcDirs = ["src/test/resources"] - } - } - testFixtures { - java { - srcDirs = ["src/test/unit"] - setIncludes(new HashSet(['org/openstreetmap/josm/plugins/mapwithai/testutils/**/*.java'])) - } - resources { - srcDirs = ["src/test/resources"] - } - } - intTest { - compileClasspath += sourceSets.main.output - compileClasspath += sourceSets.test.output - runtimeClasspath += sourceSets.main.output - runtimeClasspath += sourceSets.test.output - java { - srcDirs = ["src/test/integration"] - } - resources { - srcDirs = ["src/test/resources"] - } - } -} - - -test { - project.afterEvaluate { - jvmArgs("-javaagent:${classpath.find { it.name.contains("jmockit") }.absolutePath}") - jvmArgs("-Djunit.jupiter.extensions.autodetection.enabled=true") - jvmArgs("-Djava.awt.headless=true") - } - useJUnitPlatform() - ignoreFailures - testLogging { - exceptionFormat "full" - events "skipped", "failed" - info { - showStandardStreams true - } - } -} - -task integrationTest(type: Test) { - description = "Run integration tests" - group = "verification" - - testClassesDirs = sourceSets.intTest.output.classesDirs - classpath = sourceSets.intTest.runtimeClasspath - shouldRunAfter test - // Ignore failures -- servers may or may not be down - ignoreFailures = true -} - -check.dependsOn integrationTest - -tasks.processResources { - // Note: src/${source_set}/resources is automatically copied - // processResources uses the `main` source set. - // https://docs.gradle.org/current/userguide/building_java_projects.html#sec:java_resources - from("$projectDir/LICENSE") - from("$projectDir/README.md") -} - -jacocoTestCoverageVerification { - violationRules { - rule { - limit { - minimum = 0.80 - } - } - } -} - -spotless { - java { - eclipse().configFile "config/josm_formatting.xml" - endWithNewline() - importOrder('javax', 'java', 'org', 'com', '') - indentWithSpaces(4) - licenseHeader "// License: GPL. For details, see LICENSE file." - ratchetFrom("origin/master") - removeUnusedImports() - trimTrailingWhitespace() - } -} - -josm { - debugPort = 7055 - manifest { - setMinJavaVersion 17 - oldVersionDownloadLink 18218, "v1.9.20", new URL("https://github.com/JOSM/MapWithAI/releases/download/v1.9.20/mapwithai.jar") - oldVersionDownloadLink 17903, "v1.8.7", new URL("https://github.com/JOSM/MapWithAI/releases/download/v1.8.7/mapwithai.jar") - oldVersionDownloadLink 17084, "v1.7.1.6", new URL("https://github.com/JOSM/MapWithAI/releases/download/v1.7.1.6/mapwithai.jar") - oldVersionDownloadLink 16645, "v1.6.8", new URL("https://github.com/JOSM/MapWithAI/releases/download/v1.6.8/mapwithai.jar") - oldVersionDownloadLink 16284, "v1.5.10", new URL("https://github.com/JOSM/MapWithAI/releases/download/v1.5.10/mapwithai.jar") - oldVersionDownloadLink 16220, "v1.4.7", new URL("https://github.com/JOSM/MapWithAI/releases/download/v1.4.7/mapwithai.jar") - oldVersionDownloadLink 15820, "v1.3.11", new URL("https://github.com/JOSM/MapWithAI/releases/download/v1.3.11/mapwithai.jar") - oldVersionDownloadLink 15737, "v1.2.7", new URL("https://github.com/JOSM/MapWithAI/releases/download/v1.2.7/mapwithai.jar") - oldVersionDownloadLink 15609, "v1.1.12", new URL("https://github.com/JOSM/MapWithAI/releases/download/v1.1.12/mapwithai.jar") - oldVersionDownloadLink 15542, "v1.0.9", new URL("https://github.com/JOSM/MapWithAI/releases/download/v1.0.9/mapwithai.jar") - oldVersionDownloadLink 15233, "v0.2.14", new URL("https://github.com/JOSM/MapWithAI/releases/download/v0.2.14/mapwithai.jar") - } - i18n { - pathTransformer = getPathTransformer(project.projectDir, "gitlab.com/${gitlabGroup}/${gitlabRepositoryName}/blob") - } -} - -tasks.withType(JavaCompile) { - options.compilerArgs += [ - "-Xlint:all", - "-Xlint:-serial", - ] -} - -// Set up JaCoCo -jacoco { - toolVersion = "${versions.jacoco}" -} -jacocoTestReport { - dependsOn test - reports { - xml.required.set(true) - html.required.set(true) - } -} -check.dependsOn jacocoTestReport - -// Set up PMD -pmd { - toolVersion = versions.pmd - ignoreFailures true - incrementalAnalysis = true - ruleSets = [] - ruleSetConfig = resources.text.fromFile("$projectDir/config/pmd/ruleset.xml") - sourceSets = [sourceSets.main] -} - -// Set up SpotBugs -spotbugs { - toolVersion = versions.spotbugs - ignoreFailures = true -} -spotbugsMain { - reports { - xml.required.set(false) - html.required.set(true) - } -} - -publishing { - publications { - maven(MavenPublication) { - groupId = "org.openstreetmap.josm.plugins" - artifactId = archivesBaseName - version = project.version - - from components.java - } - } -} - -def ciJobToken = System.getenv("CI_JOB_TOKEN") -def projectId = System.getenv("CI_PROJECT_ID") -if (ciJobToken != null && projectId!= null) { - publishing.repositories.maven { - url = "https://gitlab.com/api/v4/projects/$projectId/packages/maven" - name = "gitlab" - credentials(HttpHeaderCredentials.class) { - name = "Job-Token" - value = ciJobToken - } - authentication { - create("auth", HttpHeaderAuthentication.class) - } - } -} - -sonarqube { - properties { - property "sonar.organization", "mapwithai" - property "sonar.projectKey", "mapwithai" - property "sonar.forceAuthentication", "true" - property "sonar.host.url", "https://sonarcloud.io" - property "sonar.projectDescription", properties.get("plugin.description") - property "sonar.projectVersion", project.version - property "sonar.sources", ["src"] - } -} diff --git a/build.xml b/build.xml deleted file mode 100644 index d8bfa8fd..00000000 --- a/build.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 901bc091..00000000 --- a/codecov.yml +++ /dev/null @@ -1,4 +0,0 @@ -coverage: - range: 50..100 -fixes: - - "project/::src/" diff --git a/config/josm_formatting.xml b/config/josm_formatting.xml deleted file mode 100644 index 129aad7c..00000000 --- a/config/josm_formatting.xml +++ /dev/null @@ -1,365 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/pmd/ruleset.xml b/config/pmd/ruleset.xml deleted file mode 100644 index 5d71904f..00000000 --- a/config/pmd/ruleset.xml +++ /dev/null @@ -1,197 +0,0 @@ - - - - This ruleset checks some rules that you should normally follow for the JOSM-mapwithai plugin. - Copied from josm tools/pmd/josm-ruleset.xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fonts/Roboto-Light.ttf b/fonts/Roboto-Light.ttf new file mode 100644 index 00000000..94c6bcc6 Binary files /dev/null and b/fonts/Roboto-Light.ttf differ diff --git a/gh-pages/index.html b/gh-pages/index.html deleted file mode 100644 index a1e7d322..00000000 --- a/gh-pages/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - -RapiD-plugin for JOSM - - diff --git a/gh-pages/style/default.css b/gh-pages/style/default.css deleted file mode 100644 index 25925ecb..00000000 --- a/gh-pages/style/default.css +++ /dev/null @@ -1,42 +0,0 @@ -* { - box-sizing: border-box; -} -body { - font-family: sans-serif; - background-color: #333; - color:white; - font-size: 18px; -} -h1,h2 { - font-weight:100; -} -h1 { - font-size:2.5em; -} -h2 { - font-size:1.5em; -} -.container { - text-align: center; -} -ul { - padding:0; -} -ul li { - list-style: none; -} -.container a { - color:#ddd; - text-decoration: none; - border:1px solid white; - padding:.75em; - margin:.5em; - display:inline-block; - box-shadow: 0 0 0 #36af6d; - transition:.25s ease box-shadow, .5s ease border-color, .5s ease color; -} -a:hover { - color:#fff; - border-color: #36af6d; - box-shadow: 0 0 8px #36af6d; -} diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 5c5f4494..00000000 --- a/gradle.properties +++ /dev/null @@ -1,15 +0,0 @@ -# The minimum JOSM version this plugin is compatible with (can be any numeric version -plugin.main.version = 19067 -# The JOSM version this plugin is currently compiled against -# Please make sure this version is available at https://josm.openstreetmap.de/download -# The special values "latest" and "tested" are also possible here, but not recommended. -plugin.compile.version = 19067 -plugin.canloadatruntime = true -plugin.author = Taylor Smock -plugin.class = org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin -plugin.icon = images/dialogs/mapwithai.svg -plugin.link = https://github.com/JOSM/MapWithAI -plugin.minimum.java.version = 17 -plugin.description = Allows the use of MapWithAI data in JOSM (same data as used in RapiD) - -plugin.requires = pmtiles;utilsplugin2 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 033e24c4..00000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 4baf5a11..00000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,8 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index fcb6fca1..00000000 --- a/gradlew +++ /dev/null @@ -1,248 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 6689b85b..00000000 --- a/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/img/bg.png b/img/bg.png new file mode 100644 index 00000000..5bb626a0 Binary files /dev/null and b/img/bg.png differ diff --git a/img/josm_logo.svg b/img/josm_logo.svg new file mode 100644 index 00000000..b3eedc2e --- /dev/null +++ b/img/josm_logo.svg @@ -0,0 +1,208 @@ + + + JOSM Logotype 2019 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + JOSM Logotype 2019 + 2019-08-05 + + + Diamond00744 + + + + + Public Domain + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/images/dialogs/mapwithai.svg b/img/mapwithai.svg similarity index 100% rename from src/main/resources/images/dialogs/mapwithai.svg rename to img/mapwithai.svg diff --git a/index.html b/index.html new file mode 100644 index 00000000..58e1f512 --- /dev/null +++ b/index.html @@ -0,0 +1,20 @@ + + + + + + +MapWithAI-plugin for JOSM + + +
+

Sources

+
+ diff --git a/json/blacklisted_versions.json b/json/blacklisted_versions.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/json/blacklisted_versions.json @@ -0,0 +1 @@ +[] diff --git a/json/conflation_servers.json b/json/conflation_servers.json new file mode 100644 index 00000000..a14cb507 --- /dev/null +++ b/json/conflation_servers.json @@ -0,0 +1,11 @@ +{ + "Taylor's Address Conflation Server" : { + "categories" : [ + "addresses" + ], + "description" : "Originally developed for use with local datasets, it now accepts external datasets for conflation purposes. In the event of a failure, the plugin will use the original dataset.", + "license" : "AGPL", + "source" : "https://gitlab.com/smocktaylor/serve_osm_files/", + "url" : "https://importdata.riverviewtechnologies.com/conflate" + } +} diff --git a/json/sources.json b/json/sources.json new file mode 100644 index 00000000..31ddace1 --- /dev/null +++ b/json/sources.json @@ -0,0 +1,308 @@ +{ + "(Update JOSM and MapWithAI Plugin -- currently only supported on development builds) Esri OpenStreetMap Curated Data": { + "type": "esri", + "url": "https://openstreetmap.maps.arcgis.com/sharing/rest/", + "id": "bdf6c800b3ae453b9db239e03d7c1727", + "source": "esri", + "conflate": true, + "conflation_ignore_categories": ["addresses"], + "conflationUrl": "https://rapideditor.org/maps/ml_roads?bbox={bbox}&building_source=esri&esri_id={id}", + "conflationParameters": [{ + "description": "buildings", + "enabled": true, + "parameter": "result_type=road_building_vector_xml" + }, + { + "description": "Conflate with OpenStreetMap", + "enabled": true, + "parameter": "conflate_with_osm=true" + }, + { + "description": "Type of dataset (theme)", + "enabled": true, + "permanent": true, + "parameter": "theme=ml_road_vector" + }, + { + "description": "MapWithAI Collaborator", + "enabled": true, + "permanent": true, + "parameter": "collaborator=josm" + }, + { + "description": "MapWithAI Token", + "enabled": true, + "permanent": true, + "parameter": "token=ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m" + }, + { + "description": "MapWithAI Hash", + "enabled": true, + "permanent": true, + "parameter": "hash=ASawRla3rBcwEjY4HIY" + }] + }, + "Overture": { + "type": "overture", + "url": "https://overturemaps-tiles-us-west-2-beta.s3.us-west-2.amazonaws.com/pmtiles_catalog.json", + "license": "https://docs.overturemaps.org/attribution/#places", + "category": "preview", + "provider": "Overture" + }, + "Statewide Aggregate Addresses in Colorado 2019 (Public)": { + "id": "colorado_public_aggregate_addresses", + "type": "thirdParty", + "category": "addresses", + "provider": "State of Colorado", + "countries": { + "US-CO": ["addr:housenumber"] + }, + "license": "Public Domain", + "osm_compatible": "yes", + "parameters": [], + "permission_url": "https://wiki.openstreetmap.org/wiki/Import/Colorado_Addresses", + "url": "https://importdata.riverviewtechnologies.com/coloradoAddresses/map?bbox={bbox}" + }, + "MapWithAI": { + "id": "facebook_mapwithai", + "type": "facebook", + "category": "highways;buildings;featured", + "provider": "Facebook", + "countries": { + "AE": ["highway"], + "AF": ["highway"], + "AG": ["highway"], + "AI": ["highway"], + "AL": ["highway"], + "AM": ["highway"], + "AO": ["highway"], + "AR": ["highway"], + "AT": ["highway"], + "AU": ["highway"], + "AZ": ["highway"], + "BA": ["highway"], + "BB": ["highway"], + "BD": ["highway"], + "BE": ["highway"], + "BF": ["highway"], + "BG": ["highway"], + "BI": ["highway"], + "BJ": ["highway"], + "BL": ["highway"], + "BN": ["highway"], + "BO": ["highway"], + "BR": ["highway"], + "BS": ["highway"], + "BT": ["highway"], + "BW": ["highway"], + "BY": ["highway"], + "BZ": ["highway"], + "CA": ["building", "highway"], + "CD": ["highway"], + "CF": ["highway"], + "CG": ["highway"], + "CH": ["highway"], + "CI": ["highway"], + "CL": ["highway"], + "CM": ["highway"], + "CN": ["highway"], + "CO": ["highway"], + "CR": ["highway"], + "CU": ["highway"], + "CY": ["highway"], + "CZ": ["highway"], + "DE": ["highway"], + "DJ": ["highway"], + "DK": ["highway"], + "DM": ["highway"], + "DO": ["highway"], + "DZ": ["highway"], + "EC": ["highway"], + "EE": ["highway"], + "EG": ["highway"], + "EH": ["highway"], + "ER": ["highway"], + "ES": ["highway"], + "ET": ["highway"], + "FK": ["highway"], + "FI": ["highway"], + "FJ": ["highway"], + "FR": ["highway"], + "GA": ["highway"], + "GB": ["highway"], + "GD": ["highway"], + "GE": ["highway"], + "GF": ["highway"], + "GH": ["highway"], + "GM": ["highway"], + "GN": ["highway"], + "GP": ["highway"], + "GQ": ["highway"], + "GR": ["highway"], + "GT": ["highway"], + "GW": ["highway"], + "GY": ["highway"], + "HN": ["highway"], + "HR": ["highway"], + "HT": ["highway"], + "HU": ["highway"], + "ID": ["highway"], + "IE": ["highway"], + "IL": ["highway"], + "IN": ["highway"], + "IQ": ["highway"], + "IS": ["highway"], + "IT": ["highway"], + "JM": ["highway"], + "JO": ["highway"], + "JP": ["highway"], + "KE": ["highway"], + "KG": ["highway"], + "KH": ["highway"], + "KN": ["highway"], + "KY": ["highway"], + "KR": ["highway"], + "KW": ["highway"], + "KZ": ["highway"], + "LA": ["highway"], + "LB": ["highway"], + "LC": ["highway"], + "LK": ["highway"], + "LR": ["highway"], + "LS": ["highway"], + "LT": ["highway"], + "LU": ["highway"], + "LV": ["highway"], + "LY": ["highway"], + "MA": ["highway"], + "MD": ["highway"], + "ME": ["highway"], + "MF": ["highway"], + "MG": ["highway"], + "MK": ["highway"], + "ML": ["highway"], + "MM": ["highway"], + "MN": ["highway"], + "MQ": ["highway"], + "MR": ["highway"], + "MS": ["highway"], + "MW": ["highway"], + "MX": ["highway"], + "MY": ["highway"], + "MZ": ["highway"], + "NA": ["highway"], + "NE": ["highway"], + "NG": ["highway"], + "NI": ["highway"], + "NL": ["highway"], + "NL-BQ2": ["highway"], + "NL-BQ3": ["highway"], + "NO": ["highway"], + "NP": ["highway"], + "NZ": ["highway"], + "OM": ["highway"], + "PA": ["highway"], + "PE": ["highway"], + "PF": ["highway"], + "PG": ["highway"], + "PH": ["highway"], + "PK": ["highway"], + "PL": ["highway"], + "PR": ["highway"], + "PS": ["highway"], + "PT": ["highway"], + "PY": ["highway"], + "QA": ["highway"], + "RO": ["highway"], + "RS": ["highway"], + "RS-KM": ["highway"], + "RU": ["highway"], + "RW": ["highway"], + "SA": ["highway"], + "SB": ["highway"], + "SD": ["highway"], + "SE": ["highway"], + "SG": ["highway"], + "SI": ["highway"], + "SK": ["highway"], + "SL": ["highway"], + "SN": ["highway"], + "SO": ["highway"], + "SR": ["highway"], + "SS": ["highway"], + "ST": ["highway"], + "SV": ["highway"], + "SX": ["highway"], + "SZ": ["highway"], + "TC": ["highway"], + "TD": ["highway"], + "TG": ["highway"], + "TH": ["highway"], + "TJ": ["highway"], + "TL": ["highway"], + "TM": ["highway"], + "TN": ["highway"], + "TR": ["highway"], + "TT": ["highway"], + "TW": ["highway"], + "TZ": ["building", "highway"], + "UA": ["highway"], + "UG": ["building", "highway"], + "US": ["building", "highway"], + "US-AK": ["building"], + "US-HI": ["building"], + "UY": ["highway"], + "UZ": ["highway"], + "VC": ["highway"], + "VE": ["highway"], + "VG": ["highway"], + "VN": ["highway"], + "VU": ["highway"], + "YE": ["highway"], + "ZA": ["highway"], + "ZM": ["highway"], + "ZW": ["highway"] + }, + "default": true, + "license": "ODBL", + "osm_compatible": "yes", + "parameters": [{ + "description": "buildings", + "enabled": true, + "parameter": "result_type=road_building_vector_xml" + }, + { + "description": "Conflate with OpenStreetMap", + "enabled": true, + "parameter": "conflate_with_osm=true" + }, + { + "description": "Type of dataset (theme)", + "enabled": true, + "permanent": true, + "parameter": "theme=ml_road_vector" + }, + { + "description": "MapWithAI Collaborator", + "enabled": true, + "permanent": true, + "parameter": "collaborator=josm" + }, + { + "description": "MapWithAI Token", + "enabled": true, + "permanent": true, + "parameter": "token=ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m" + }, + { + "description": "MapWithAI Hash", + "enabled": true, + "permanent": true, + "parameter": "hash=ASawRla3rBcwEjY4HIY" + }], + "permission_url": "https://github.com/facebookmicrosites/Open-Mapping-At-Facebook/wiki/FAQ", + "terms_of_use_url": "https://rapideditor.org/doc/license/MapWithAILicense.pdf", + "privacy_policy_url": "https://rapideditor.org/doc/license/MapWithAIPrivacyPolicy.pdf#page=3", + "url": "https://rapideditor.org/maps/ml_roads?bbox={bbox}" + } +} diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 8a0a9214..00000000 --- a/pom.xml +++ /dev/null @@ -1,193 +0,0 @@ - - 4.0.0 - org.openstreetmap.josm.plugins - MapWithAI - 1.0-SNAPSHOT - - 1.49.a - 7.5.0 - 0.8.12 - 10.18.1 - 4.8.6 - - - - JOSM-releases - https://josm.openstreetmap.de/repository/releases/ - - - JOSM-snapshots - https://josm.openstreetmap.de/repository/snapshots/ - - - - - - org.junit - junit-bom - 5.11.1 - pom - import - - - - - - org.openstreetmap.josm - josm - 1.5-SNAPSHOT - provided - - - org.openstreetmap.josm.plugins - pmtiles - 1.0-SNAPSHOT - provided - - - org.openstreetmap.josm.plugins - utilsplugin2 - 1.0-SNAPSHOT - provided - - - org.openstreetmap.josm - josm-unittest - 1.5-SNAPSHOT - test - - - org.wiremock - wiremock - 3.9.1 - test - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-params - test - - - junit - junit - 4.13.2 - test - - - org.awaitility - awaitility - 4.2.2 - test - - - nl.jqno.equalsverifier - equalsverifier - 3.17 - test - - - org.jmockit - jmockit - 1.49.a - test - - - jakarta.json - jakarta.json-api - 2.1.3 - provided - - - - src/test/unit - - - org.apache.maven.plugins - maven-compiler-plugin - 3.13.0 - - 17 - - - - com.diffplug.spotless - spotless-maven-plugin - 2.43.0 - - - - 4.21 - ${project.basedir}/../00_core_tools/eclipse/formatter.xml - - - - // License: GPL. For details, see LICENSE file. - - - src/main/java/**/*.java - src/test/unit/**/*.java - src/test/integration/**/*.java - - - - - - - check - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.5.0 - - - enforce-maven - - enforce - - - - - - - 3.6.3 - - - - - - - maven-surefire-plugin - 3.2.5 - - 1 - -javaagent:"${settings.localRepository}"/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar - - - file.encoding = UTF-8 - java.locale.providers = SPI,CLDR - junit.jupiter.extensions.autodetection.enabled = true - junit.jupiter.execution.parallel.enabled = true - - - - src/test/build/config/josm.home - src/test/resources - true - Monocle - Headless - sw - - - - - - diff --git a/scripts/sources.js b/scripts/sources.js new file mode 100644 index 00000000..4b9bb514 --- /dev/null +++ b/scripts/sources.js @@ -0,0 +1,53 @@ +function addCommaSpace(text) { + var spacedText = ""; + for (i of text) { + spacedText += i; + if (i == ",") { + spacedText += " "; + } + } + return spacedText; +} +function createTable(sources) { + var div = document.getElementsByClassName("mapwithaisourcetable")[0]; + var table = document.createElement("table"); + var tablebody = document.createElement("tbody"); + var tablehead = document.createElement("thead"); + var tableheadrow = document.createElement("tr"); + for (header of ["Source", "URL", "Parameters", "Countries", "License", "OSM Usable"]) { + var th = document.createElement("th"); + th.textContent = header; + tableheadrow.appendChild(th); + } + tablehead.appendChild(tableheadrow); + + for (source in sources) { + var row = document.createElement("tr"); + var sourcetd = document.createElement("td"); + sourcetd.textContent = source; + row.appendChild(sourcetd); + for (columnName of ["url", "parameters", "countries", "license", "osm_compatible"]) { + var column = document.createElement("td"); + column.textContent = sources[source][columnName]; + if (column.textContent == "[object Object]") { + column.textContent = JSON.stringify(sources[source][columnName]); + } + column.textContent = addCommaSpace(column.textContent); + row.appendChild(column); + } + tablebody.appendChild(row); + } + + table.appendChild(tablehead); + table.appendChild(tablebody); + div.appendChild(table); + console.log(sources); +} + +function init() { + fetch("json/sources.json") + .then(response => response.json()) + .then(json => createTable(json)); +} + +init(); diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPlugin.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPlugin.java deleted file mode 100644 index 957b93bd..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPlugin.java +++ /dev/null @@ -1,210 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.swing.JMenuItem; - -import org.openstreetmap.josm.actions.JosmAction; -import org.openstreetmap.josm.actions.PreferencesAction; -import org.openstreetmap.josm.data.validation.OsmValidator; -import org.openstreetmap.josm.data.validation.Test; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.MainMenu; -import org.openstreetmap.josm.gui.MapFrame; -import org.openstreetmap.josm.gui.download.DownloadDialog; -import org.openstreetmap.josm.gui.download.DownloadSelection; -import org.openstreetmap.josm.gui.download.OSMDownloadSource; -import org.openstreetmap.josm.gui.preferences.PreferenceSetting; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.io.remotecontrol.RequestProcessor; -import org.openstreetmap.josm.plugins.Plugin; -import org.openstreetmap.josm.plugins.PluginInformation; -import org.openstreetmap.josm.plugins.mapwithai.backend.DownloadListener; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIAction; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAILayer; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIMoveAction; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIObject; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIRemoteControl; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIUploadHook; -import org.openstreetmap.josm.plugins.mapwithai.backend.MergeDuplicateWaysAction; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.PreConflatedDataUtils; -import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.ConnectingNodeInformationTest; -import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.RoutingIslandsTest; -import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.StreetAddressOrder; -import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.StreetAddressTest; -import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.StubEndsTest; -import org.openstreetmap.josm.plugins.mapwithai.gui.MapWithAIMenu; -import org.openstreetmap.josm.plugins.mapwithai.gui.download.MapWithAIDownloadOptions; -import org.openstreetmap.josm.plugins.mapwithai.gui.download.MapWithAIDownloadSourceType; -import org.openstreetmap.josm.plugins.mapwithai.gui.preferences.MapWithAIPreferences; -import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig; -import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIUrls; -import org.openstreetmap.josm.plugins.mapwithai.tools.MapPaintUtils; -import org.openstreetmap.josm.plugins.mapwithai.tools.MapWithAICopyProhibit; -import org.openstreetmap.josm.spi.preferences.Config; -import org.openstreetmap.josm.tools.Destroyable; -import org.openstreetmap.josm.tools.Logging; - -/** - * The POJO entry point for the plugin - */ -public final class MapWithAIPlugin extends Plugin implements Destroyable { - /** The name of the plugin */ - public static final String NAME = "MapWithAI"; - - static final String PAINTSTYLE_PREEXISTS = NAME.concat(".paintstyleprexists"); - private static String versionInfo; - - private final PreferenceSetting preferenceSetting; - - private final List destroyables; - - private final MapWithAIMenu mapwithaiMenu; - - private static final Map, Boolean> MENU_ENTRIES = new LinkedHashMap<>(); - static { - MENU_ENTRIES.put(MapWithAIAction.class, false); - MENU_ENTRIES.put(MapWithAIMoveAction.class, false); - MENU_ENTRIES.put(MergeDuplicateWaysAction.class, true); - } - - private static final List> VALIDATORS = Arrays.asList(RoutingIslandsTest.class, - ConnectingNodeInformationTest.class, StubEndsTest.class, StreetAddressTest.class, StreetAddressOrder.class); - - public MapWithAIPlugin(PluginInformation info) { - super(info); - - MapWithAIConfig.setUrlsProvider(MapWithAIUrls.getInstance()); - - preferenceSetting = new MapWithAIPreferences(); - - // Add MapWithAI specific menu - final var dataMenu = MainApplication.getMenu().dataMenu; - mapwithaiMenu = new MapWithAIMenu(); - - dataMenu.add(mapwithaiMenu); - for (final Map.Entry, Boolean> entry : MENU_ENTRIES.entrySet()) { - if (Arrays.stream(mapwithaiMenu.getMenuComponents()).filter(JMenuItem.class::isInstance) - .map(JMenuItem.class::cast) - .noneMatch(component -> entry.getKey().equals(component.getAction().getClass()))) { - try { - MainMenu.add(mapwithaiMenu, entry.getKey().getDeclaredConstructor().newInstance(), - entry.getValue()); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | NoSuchMethodException | SecurityException e) { - Logging.debug(e); - } - } - } - - // Add the preferences last to data - final var preferenceAction = PreferencesAction.forPreferenceTab(tr("MapWithAI Preferences"), - tr("MapWithAI Preferences"), MapWithAIPreferences.class); - MainMenu.add(mapwithaiMenu, preferenceAction); - - VALIDATORS.forEach(clazz -> { - if (!OsmValidator.getAllAvailableTestClasses().contains(clazz)) { - OsmValidator.addTest(clazz); - } - }); - - if (!Config.getPref().getKeySet().contains(PAINTSTYLE_PREEXISTS)) { - Config.getPref().putBoolean(PAINTSTYLE_PREEXISTS, MapPaintUtils.checkIfMapWithAIPaintStyleExists()); - } - - MapPaintUtils.addMapWithAIPaintStyles(); - - destroyables = new ArrayList<>(); - - setVersionInfo(info.localversion); - RequestProcessor.addRequestHandlerClass("mapwithai", MapWithAIRemoteControl.class); - new MapWithAIRemoteControl(); // instantiate to get action into Remote Control Preferences - destroyables.add(new MapWithAIUploadHook(info)); - destroyables.add(new PreConflatedDataUtils()); - mapFrameInitialized(null, MainApplication.getMap()); - OSMDownloadSource.addDownloadType(new MapWithAIDownloadSourceType()); - MainApplication.worker.execute(() -> UpdateProd.doProd(info.mainversion)); - // Preload the MapWithAILayerInfo for the JOSM download window - // This reduces the amount of time taken for first button click by 100ms. - // Don't use the worker thread to avoid blocking user downloads - MapWithAIDataUtils.getForkJoinPool().execute(MapWithAILayerInfo::getInstance); - - destroyables.add(new MapWithAICopyProhibit()); - } - - @Override - public void addDownloadSelection(List list) { - // Run in EDT to avoid blocking (has to be run before MapWithAIDownloadOptions - // so its already initialized) - GuiHelper.runInEDT(MapWithAILayerInfo::getInstance); - final var mapWithAIDownloadOptions = new MapWithAIDownloadOptions(); - MainApplication.worker - .execute(() -> GuiHelper.runInEDT(() -> mapWithAIDownloadOptions.addGui(DownloadDialog.getInstance()))); - destroyables.add(mapWithAIDownloadOptions); - } - - @Override - public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { - final var mapWithAIObject = destroyables.stream().filter(MapWithAIObject.class::isInstance) - .map(MapWithAIObject.class::cast).findFirst().orElseGet(MapWithAIObject::new); - if ((oldFrame != null) && (oldFrame.statusLine != null)) { - mapWithAIObject.removeMapStatus(oldFrame.statusLine); - } - if ((newFrame != null) && (newFrame.statusLine != null)) { - mapWithAIObject.addMapStatus(newFrame.statusLine); - } - if (!destroyables.contains(mapWithAIObject)) { - destroyables.add(mapWithAIObject); - } - } - - @Override - public PreferenceSetting getPreferenceSetting() { - return preferenceSetting; - } - - /** - * The current version for the plugin - * - * @return The version information of the plugin - */ - public static String getVersionInfo() { - return versionInfo; - } - - private static void setVersionInfo(String newVersionInfo) { - versionInfo = newVersionInfo; - } - - /** - * This is so that if JOSM ever decides to support updating plugins without - * restarting, I don't have to do anything (hopefully -- I might have to change - * the interface and method). Not currently used... (October 16, 2019) - */ - @Override - public void destroy() { - MainApplication.getMenu().dataMenu.remove(this.mapwithaiMenu); - - MainApplication.getLayerManager().getLayersOfType(MapWithAILayer.class) - .forEach(layer -> MainApplication.getLayerManager().removeLayer(layer)); - - if (!Config.getPref().getBoolean(PAINTSTYLE_PREEXISTS)) { - MapPaintUtils.removeMapWithAIPaintStyles(); - } - - destroyables.forEach(Destroyable::destroy); - OSMDownloadSource.removeDownloadType(OSMDownloadSource.getDownloadType(MapWithAIDownloadSourceType.class)); - VALIDATORS.forEach(OsmValidator::removeTest); - DownloadListener.destroyAll(); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/UpdateProd.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/UpdateProd.java deleted file mode 100644 index 73a6fd50..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/UpdateProd.java +++ /dev/null @@ -1,49 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.JOptionPane; - -import org.openstreetmap.josm.data.Version; -import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.spi.preferences.Config; -import org.openstreetmap.josm.tools.OpenBrowser; - -/** - * Prod users to update JOSM (only increment to new compile version) - * - * @author Taylor Smock - */ -public final class UpdateProd { - private UpdateProd() { - // Hide constructor - } - - /** - * Prod user to update JOSM - * - * @param nextMinVersion The next version of JOSM that is supported by MapWithAI - * @return {@code true} if an update is needed - */ - public static boolean doProd(int nextMinVersion) { - if (nextMinVersion > Version.getInstance().getVersion() - && Version.getInstance().getVersion() != Version.JOSM_UNKNOWN_VERSION) { - int doUpdate = ConditionalOptionPaneUtil.showOptionDialog( - MapWithAIPlugin.NAME.concat(".ignore_next_version"), MainApplication.getMainFrame(), - tr("Please update JOSM -- {0} {1} is the last {0} version to support JOSM {2}", - MapWithAIPlugin.NAME, MapWithAIPlugin.getVersionInfo(), - Integer.toString(Version.getInstance().getVersion())), - tr("{0}: Please update JOSM", MapWithAIPlugin.NAME), JOptionPane.YES_NO_OPTION, - JOptionPane.INFORMATION_MESSAGE, new Object[] { tr("Update"), tr("Don''t Update") }, tr("Update")); - if (doUpdate == 0) { - OpenBrowser.displayUrl("https://josm.openstreetmap.de"); - } - return true; - } - Config.getPref().put(MapWithAIPlugin.NAME.concat(".ignore_next_version"), null); - Config.getPref().put(MapWithAIPlugin.NAME.concat(".ignore_next_version.value"), null); - return false; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/actions/AddMapWithAILayerAction.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/actions/AddMapWithAILayerAction.java deleted file mode 100644 index 40c2c204..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/actions/AddMapWithAILayerAction.java +++ /dev/null @@ -1,152 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.actions; - -import static java.util.function.Predicate.not; -import static org.openstreetmap.josm.gui.help.HelpUtil.ht; - -import java.awt.event.ActionEvent; -import java.io.Serial; -import java.util.ArrayList; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ForkJoinTask; - -import org.openstreetmap.josm.actions.AdaptableAction; -import org.openstreetmap.josm.actions.AddImageryLayerAction; -import org.openstreetmap.josm.actions.JosmAction; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.OsmData; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.gui.preferences.ToolbarPreferences; -import org.openstreetmap.josm.gui.progress.NullProgressMonitor; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAILayer; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.tools.ImageProvider; -import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; -import org.openstreetmap.josm.tools.ImageResource; -import org.openstreetmap.josm.tools.JosmRuntimeException; -import org.openstreetmap.josm.tools.Logging; - -/** - * Action displayed in MapWithAI menu to add data to the MapWithAI layer. - * Largely copied from {@link AddImageryLayerAction}. - */ -public class AddMapWithAILayerAction extends JosmAction implements AdaptableAction { - @Serial - private static final long serialVersionUID = 1403912860658467920L; - private final transient MapWithAIInfo info; - - /** - * Constructs a new {@code AddMapWithAILayerAction} for the given - * {@code MapWithAIInfo}. If an http:// icon is specified, it is fetched - * asynchronously. - * - * @param info The source info - */ - public AddMapWithAILayerAction(MapWithAIInfo info) { - super(info.getName(), /* ICON */"imagery_menu", info.getToolTipText(), null, true, - ToolbarPreferences.IMAGERY_PREFIX + info.getToolbarName(), false); - setHelpId(ht("/Preferences/Imagery")); - this.info = info; - installAdapters(); - - // change toolbar icon from if specified - final var icon = info.getIcon(); - if (icon != null) { - final var future = new ImageProvider(icon).setOptional(true).getResourceAsync(result -> { - if (result != null) { - GuiHelper.runInEDT(() -> result.attachImageIcon(this)); - } - }); - try { - future.get(); - } catch (InterruptedException e) { - Logging.error(e); - Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - Logging.error(e); - } - } else { - try { - final var resource = new ImageResource( - this.info.getSourceCategory().getIcon(ImageSizes.MENU).getImage()); - resource.attachImageIcon(this); - } catch (JosmRuntimeException e) { - // Eclipse doesn't like giving applications their resources... - if (!e.getMessage().contains("failed to locate image")) { - throw e; - } - } - } - } - - @Override - public void actionPerformed(ActionEvent e) { - if (isEnabled()) { - MainApplication.worker.execute(() -> realRun(this.info)); - } - } - - /** - * Run the download tasks. This should be run off of the EDT, see #23529. - * - * @param info The external data to download - */ - private static void realRun(MapWithAIInfo info) { - MapWithAILayer layer = MapWithAIDataUtils.getLayer(false); - final DataSet ds; - final OsmData boundsSource; - final var dataLayer = getDataLayer(); - if (layer != null && !layer.getData().getDataSourceBounds().isEmpty()) { - ds = layer.getDataSet(); - boundsSource = ds; - } else if (dataLayer != null && !dataLayer.getDataSet().getDataSourceBounds().isEmpty()) { - boundsSource = dataLayer.getDataSet(); - layer = MapWithAIDataUtils.getLayer(true); - ds = layer.getDataSet(); - } else { - boundsSource = null; - ds = null; - } - if (boundsSource != null && ds != null) { - final var pool = MapWithAIDataUtils.getForkJoinPool(); - final var forkJoinTasks = new ArrayList>(boundsSource.getDataSourceBounds().size()); - for (var b : boundsSource.getDataSourceBounds()) { - final var task = MapWithAIDataUtils.download(NullProgressMonitor.INSTANCE, b, info, - MapWithAIDataUtils.MAXIMUM_SIDE_DIMENSIONS); - forkJoinTasks.add(task); - pool.execute(task); - } - for (var task : forkJoinTasks) { - ds.mergeFrom(task.join()); - } - } - if (layer != null) { - layer.addDownloadedInfo(info); - } - } - - private static OsmDataLayer getDataLayer() { - return MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream() - .filter(not(MapWithAILayer.class::isInstance)).findFirst().orElse(null); - } - - @Override - protected boolean listenToSelectionChange() { - return false; - } - - @Override - protected void updateEnabledState() { - setEnabled(!info.isBlacklisted() && MainApplication.getLayerManager().getActiveDataLayer() != null - && (MapWithAIDataUtils.getLayer(false) == null - || !MapWithAIDataUtils.getLayer(false).hasDownloaded(info))); - } - - @Override - public String toString() { - return "AddMapWithAILayerAction [info=" + info + ']'; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/BoundingBoxMapWithAIDownloader.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/BoundingBoxMapWithAIDownloader.java deleted file mode 100644 index 2b55e1aa..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/BoundingBoxMapWithAIDownloader.java +++ /dev/null @@ -1,522 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.JOptionPane; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.net.MalformedURLException; -import java.net.SocketTimeoutException; -import java.net.URI; -import java.net.URL; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.DataSource; -import org.openstreetmap.josm.data.imagery.ImageryInfo; -import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile; -import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapboxVectorTileSource; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.IPrimitive; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.PrimitiveId; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.RelationMember; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.data.vector.VectorNode; -import org.openstreetmap.josm.data.vector.VectorPrimitive; -import org.openstreetmap.josm.data.vector.VectorRelation; -import org.openstreetmap.josm.data.vector.VectorRelationMember; -import org.openstreetmap.josm.data.vector.VectorWay; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.Notification; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.gui.progress.NullProgressMonitor; -import org.openstreetmap.josm.gui.progress.ProgressMonitor; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.io.BoundingBoxDownloader; -import org.openstreetmap.josm.io.GeoJSONReader; -import org.openstreetmap.josm.io.IllegalDataException; -import org.openstreetmap.josm.io.OsmApiException; -import org.openstreetmap.josm.io.OsmReader; -import org.openstreetmap.josm.io.OsmTransferException; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIConflationCategory; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType; -import org.openstreetmap.josm.plugins.mapwithai.tools.MapPaintUtils; -import org.openstreetmap.josm.plugins.pmtiles.data.imagery.PMTilesImageryInfo; -import org.openstreetmap.josm.plugins.pmtiles.gui.layers.PMTilesImageSource; -import org.openstreetmap.josm.plugins.pmtiles.lib.DirectoryCache; -import org.openstreetmap.josm.plugins.pmtiles.lib.Header; -import org.openstreetmap.josm.plugins.pmtiles.lib.PMTiles; -import org.openstreetmap.josm.spi.preferences.Config; -import org.openstreetmap.josm.tools.HttpClient; -import org.openstreetmap.josm.tools.JosmRuntimeException; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Utils; - -import jakarta.json.Json; -import jakarta.json.JsonValue; -import jakarta.json.stream.JsonParser; - -/** - * A bounding box downloader for MapWithAI - * - * @author Taylor Smock - */ -public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { - private final String url; - private final boolean crop; - private final int start; - - private static long lastErrorTime; - - private final Bounds downloadArea; - private final MapWithAIInfo info; - private DataConflationSender dcs; - - private static final int DEFAULT_TIMEOUT = 50_000; // 50 seconds - - /** - * Create a new {@link BoundingBoxMapWithAIDownloader} object - * - * @param downloadArea The area to download - * @param info The info to use to get the url to download - * @param crop Whether or not to crop the download area - */ - public BoundingBoxMapWithAIDownloader(Bounds downloadArea, MapWithAIInfo info, boolean crop) { - this(downloadArea, info, crop, 0); - } - - /** - * Create a new {@link BoundingBoxMapWithAIDownloader} object - * - * @param downloadArea The area to download - * @param info The info to use to get the url to download - * @param crop Whether or not to crop the download area - * @param start The number of objects to skip (Esri only please) - */ - private BoundingBoxMapWithAIDownloader(Bounds downloadArea, MapWithAIInfo info, boolean crop, int start) { - super(downloadArea); - this.info = info; - this.url = info.getUrlExpanded(); - this.crop = crop; - this.downloadArea = downloadArea; - this.start = start; - } - - @Override - protected String getRequestForBbox(double lon1, double lat1, double lon2, double lat2) { - if (url.contains("{x}") && url.contains("{y}") && url.contains("{z}")) { - final var tile = TileXYZ.tileFromBBox(lon1, lat1, lon2, lat2); - return getRequestForTile(tile); - } - var current = url.replace("{bbox}", Double.toString(lon1) + ',' + lat1 + ',' + lon2 + ',' + lat2) - .replace("{xmin}", Double.toString(lon1)).replace("{ymin}", Double.toString(lat1)) - .replace("{xmax}", Double.toString(lon2)).replace("{ymax}", Double.toString(lat2)); - boolean hasQuery = !Optional.ofNullable(URI.create(current).getRawQuery()).map(String::isEmpty).orElse(true); - - if (crop) { - current += (hasQuery ? '&' : '?') + "crop_bbox=" - + DetectTaskingManagerUtils.getTaskingManagerBounds().toBBox().toStringCSV(","); - hasQuery = true; - } - if (this.info.getSourceType() == MapWithAIType.ESRI_FEATURE_SERVER && !this.info.isConflated()) { - current += (hasQuery ? '&' : '?') + "resultOffset=" + this.start; - } - return current; - } - - private String getRequestForTile(TileXYZ tile) { - return url.replace("{x}", Long.toString(tile.x())).replace("{y}", Long.toString(tile.y())).replace("{z}", - Long.toString(tile.z())); - } - - @Override - public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException { - long startTime = System.nanoTime(); - try { - var externalData = super.parseOsm(progressMonitor); - // Don't call conflate code unnecessarily - if ((this.info.getSourceType() != MapWithAIType.ESRI_FEATURE_SERVER || this.start == 0) - && Boolean.TRUE.equals(MapWithAIInfo.THIRD_PARTY_CONFLATE.get()) && !this.info.isConflated() - && !MapWithAIConflationCategory.conflationUrlFor(this.info.getCategory()).isEmpty()) { - if (externalData.getDataSourceBounds().isEmpty()) { - externalData.addDataSource(new DataSource(this.downloadArea, "External Data")); - } - final var toConflate = getConflationData(this.downloadArea); - dcs = new DataConflationSender(this.info.getCategory(), toConflate, externalData); - dcs.run(); - try { - final var conflatedData = dcs.get(30, TimeUnit.SECONDS); - if (conflatedData != null) { - externalData = conflatedData; - } - } catch (InterruptedException e) { - Logging.error(e); - Thread.currentThread().interrupt(); - } catch (ExecutionException | TimeoutException e) { - Logging.error(e); - } - } - MapPaintUtils.addSourcesToPaintStyle(externalData); - return externalData; - } catch (OsmApiException e) { - if (!(e.getResponseCode() == 504 && (System.nanoTime() - lastErrorTime) < 120_000_000_000L)) { - throw e; - } - } catch (OsmTransferException e) { - if (e.getCause() instanceof SocketTimeoutException && (System.nanoTime() - startTime) > 30_000_000_000L) { - updateLastErrorTime(System.nanoTime()); - final var note = new Notification(); - GuiHelper.runInEDT(() -> note.setContent(tr( - "Attempting to download data in the background. This may fail or succeed in a few minutes."))); - GuiHelper.runInEDT(note::show); - } else if (e.getCause() instanceof IllegalDataException) { - final Instant lastUpdated; - final var now = Instant.now(); - synchronized (BoundingBoxMapWithAIDownloader.class) { - lastUpdated = Instant.ofEpochSecond(Config.getPref().getLong("mapwithai.layerinfo.lastupdated", 0)); - Config.getPref().putLong("mapwithai.layerinfo.lastupdated", now.getEpochSecond()); - } - // Only force an update if the last update time is sufficiently old. - if (now.toEpochMilli() - lastUpdated.toEpochMilli() > TimeUnit.MINUTES.toMillis(10)) { - MapWithAILayerInfo.getInstance().loadDefaults(true, MapWithAIDataUtils.getForkJoinPool(), false, - () -> GuiHelper.runInEDT(() -> { - final var notification = new Notification(tr( - "MapWithAI layers reloaded. Removing and re-adding the MapWithAI layer may be necessary.")); - notification.setIcon(JOptionPane.INFORMATION_MESSAGE); - notification.setDuration(Notification.TIME_LONG); - notification.show(); - })); - } - } else { - throw e; - } - } - // Just in case something happens, try again... - final var ds = new DataSet(); - final var runnable = new GetDataRunnable(downloadArea, ds, NullProgressMonitor.INSTANCE); - runnable.setMapWithAIInfo(info); - MainApplication.worker.execute(() -> { - try { - // It seems that the server has issues if I make a request soon - // after the failing request due to a timeout. - TimeUnit.SECONDS.sleep(10); - } catch (InterruptedException e1) { - Thread.currentThread().interrupt(); - } - runnable.compute(); - }); - - MapPaintUtils.addSourcesToPaintStyle(ds); - return ds; - } - - /** - * Get data to send to the conflation server - * - * @param bound The bounds that we are sending to the server - * @return The dataset to send to the server - */ - private static DataSet getConflationData(Bounds bound) { - final var area = DataSource.getDataSourceArea(Collections.singleton(new DataSource(bound, ""))); - if (area != null) { - final var layers = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream().filter( - l -> l.getDataSet().getDataSourceBounds().stream().anyMatch(b -> area.contains(bound.asRect()))) - .toList(); - return layers.stream().max(Comparator.comparingInt(l -> l.getDataSet().allPrimitives().size())) - .map(OsmDataLayer::getDataSet).orElse(null); - } - return null; - } - - private static void updateLastErrorTime(long time) { - lastErrorTime = time; - } - - @Override - protected DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException { - DataSet ds; - final var contentType = this.activeConnection.getResponse().getContentType(); - if (this.info.getSourceType() == MapWithAIType.PMTILES - || this.info.getSourceType() == MapWithAIType.MAPBOX_VECTOR_TILE) { - ds = readMvt(source, progressMonitor); - } else if (Arrays.asList("text/json", "application/json", "application/geo+json").contains(contentType) - // Fall back to Esri Feature Server check. They don't always indicate a json - // return type. :( - || (this.info.getSourceType() == MapWithAIType.ESRI_FEATURE_SERVER && !this.info.isConflated())) { - ds = readJson(source, progressMonitor); - } else { - // Fall back to XML parsing - ds = OsmReader.parseDataSet(source, progressMonitor, OsmReader.Options.CONVERT_UNKNOWN_TO_TAGS, - OsmReader.Options.SAVE_ORIGINAL_ID); - } - if (url != null && info.getUrl() != null && !info.getUrl().trim().isEmpty()) { - if (info.getSource() != null) { - GetDataRunnable.addSourceTag(ds, info.getSource()); - } else { - GetDataRunnable.addMapWithAISourceTag(ds, getMapWithAISourceTag(info)); - } - } - GetDataRunnable.cleanup(ds, downloadArea, info); - return ds; - } - - private DataSet readMvt(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException { - final DataSet ds; - final TileSource tileSource; - final List tiles; - final Header header; - final DirectoryCache cachedDirectories; - if (this.info.getSourceType() == MapWithAIType.PMTILES) { - try { - header = PMTiles.readHeader(URI.create(this.url)); - final var root = PMTiles.readRootDirectory(header); - tiles = TileXYZ.tilesFromBBox(header.maxZoom(), this.downloadArea).toList(); - cachedDirectories = new DirectoryCache(root); - tileSource = new PMTilesImageSource(new PMTilesImageryInfo(header)); - } catch (IOException e) { - throw new IllegalDataException(e); - } - } else { - header = null; - cachedDirectories = null; - // Assume the source is added by the user - final int zoom; - if (this.info.getMaxZoom() == 0) { - var z = 18; - for (; z > 0; z--) { - final var tileOptional = TileXYZ.tilesFromBBox(z, this.downloadArea).findFirst(); - if (tileOptional.isPresent()) { - final var tile = tileOptional.get(); - try { - final var responseHeader = java.net.http.HttpClient.newBuilder() - .followRedirects(java.net.http.HttpClient.Redirect.NORMAL).build() - .send(HttpRequest.newBuilder().uri(URI.create(this.getRequestForTile(tile))) - .method("HEAD", HttpRequest.BodyPublishers.noBody()).build(), - HttpResponse.BodyHandlers.discarding()); - if (responseHeader.statusCode() >= 200 && responseHeader.statusCode() < 300) { - break; - } - } catch (IOException e) { - Logging.trace(e); - } catch (InterruptedException e) { - Logging.trace(e); - Thread.currentThread().interrupt(); - return null; - } - } - } - zoom = z; - } else { - zoom = this.info.getMaxZoom(); - } - tiles = TileXYZ.tilesFromBBox(zoom, this.downloadArea).toList(); - tileSource = new MapboxVectorTileSource(new ImageryInfo(this.url, this.url)); - } - ds = new DataSet(); - final var currentBounds = new Bounds(this.downloadArea); - progressMonitor.beginTask(tr("Downloading data"), 2 * tiles.size()); - for (TileXYZ tileXYZ : tiles) { - try { - final var hilbert = PMTiles.convertToHilbert(tileXYZ.z(), tileXYZ.x(), tileXYZ.y()); - final var data = this.info.getSourceType() == MapWithAIType.PMTILES - ? new ByteArrayInputStream(PMTiles.readData(header, hilbert, cachedDirectories)) - : getInputStream(getRequestForTile(tileXYZ), progressMonitor.createSubTaskMonitor(1, true)); - final var dataSet = loadTile(tileSource, tileXYZ, data); - ds.mergeFrom(dataSet, progressMonitor.createSubTaskMonitor(1, true)); - tileXYZ.expandBounds(currentBounds); - } catch (OsmTransferException | IOException e) { - progressMonitor.finishTask(); - throw new IllegalDataException(e); - } - } - progressMonitor.finishTask(); - ds.addDataSource(new DataSource(currentBounds, this.url)); - return ds; - } - - private static DataSet loadTile(TileSource tileSource, TileXYZ tileXYZ, InputStream actualSource) - throws IllegalDataException { - - final var tile = new MVTTile(tileSource, tileXYZ.x(), tileXYZ.y(), tileXYZ.z()); - try { - tile.loadImage(actualSource); - } catch (IOException e) { - throw new IllegalDataException(e); - } - final var ds = new DataSet(); - final var primitiveMap = new HashMap(tile.getData().getAllPrimitives().size()); - for (Class clazz : Arrays.asList(VectorNode.class, VectorWay.class, - VectorRelation.class)) { - for (VectorPrimitive p : Utils.filteredCollection(tile.getData().getAllPrimitives(), clazz)) { - final OsmPrimitive osmPrimitive; - if (p instanceof VectorNode node) { - osmPrimitive = new Node(node.getCoor()); - osmPrimitive.putAll(node.getKeys()); - } else if (p instanceof VectorWay way) { - final var tWay = new Way(); - for (VectorNode node : way.getNodes()) { - tWay.addNode((Node) primitiveMap.get(node)); - } - tWay.putAll(way.getKeys()); - osmPrimitive = tWay; - } else if (p instanceof VectorRelation vectorRelation) { - final var tRelation = new Relation(); - for (VectorRelationMember member : vectorRelation.getMembers()) { - tRelation.addMember(new RelationMember(member.getRole(), primitiveMap.get(member.getMember()))); - } - tRelation.putAll(vectorRelation.getKeys()); - osmPrimitive = tRelation; - } else { - throw new IllegalDataException("Unknown vector data type: " + p); - } - ds.addPrimitive(osmPrimitive); - primitiveMap.put(p, osmPrimitive); - } - } - return ds; - } - - private DataSet readJson(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException { - final DataSet ds; - // Rather unfortunately, we need to read the entire json in order to figure out - // if we need to make additional calls - try (var reader = Json.createReader(source)) { - final var structure = reader.read(); - try (var bais = new ByteArrayInputStream(structure.toString().getBytes(StandardCharsets.UTF_8))) { - ds = GeoJSONReader.parseDataSet(bais, progressMonitor); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - /* We should only call this from the "root" call */ - if (this.start == 0 && structure.getValueType() == JsonValue.ValueType.OBJECT) { - final var serverObj = structure.asJsonObject(); - final boolean exceededTransferLimit = serverObj.entrySet().stream() - .filter(entry -> "properties".equals(entry.getKey()) - && entry.getValue().getValueType() == JsonValue.ValueType.OBJECT) - .map(Map.Entry::getValue).map(JsonValue::asJsonObject) - .map(obj -> obj.getBoolean("exceededTransferLimit", false)).findFirst().orElse(false); - if (exceededTransferLimit && this.info.getSourceType() == MapWithAIType.ESRI_FEATURE_SERVER) { - final int size = serverObj.getJsonArray("features").size(); - final var other = this.getAdditionalEsriData(progressMonitor, - this.getRequestForBbox(this.lon1, this.lat1, this.lon2, this.lat2), size); - ds.mergeFrom(other, progressMonitor.createSubTaskMonitor(0, false)); - } - } - } - if (info.getReplacementTags() != null) { - GetDataRunnable.replaceKeys(ds, info.getReplacementTags()); - } - return ds; - } - - private DataSet getAdditionalEsriData(ProgressMonitor progressMonitor, String baseUrl, int size) { - final var returnDs = new DataSet(); - try { - final var client = HttpClient.create(new URL(baseUrl + "&returnCountOnly=true")); - int objects = Integer.MIN_VALUE; - try (var is = client.connect().getContent(); var parser = Json.createParser(is)) { - while (parser.hasNext()) { - final var event = parser.next(); - if (event == JsonParser.Event.START_OBJECT) { - final var objCount = parser.getObjectStream() - .filter(entry -> "properties".equals(entry.getKey())) - .map(entry -> entry.getValue().asJsonObject()) - .mapToInt(properties -> properties.getInt("count")).findFirst(); - if (objCount.isPresent()) { - objects = objCount.getAsInt(); - break; - } - } - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } finally { - client.disconnect(); - } - // Zero indexed. Esri uses 2000 as the limit. 0-1999 is 2000 objects, so we want - // to start at 2000 for the next round. - try { - progressMonitor.beginTask(tr("Downloading additional data"), objects); - // We have already downloaded some of the objects. Set the ticks. - progressMonitor.worked(size); - while (progressMonitor.getTicks() < progressMonitor.getTicksCount() - 1) { - final var next = new BoundingBoxMapWithAIDownloader(this.downloadArea, this.info, this.crop, - this.start + size).parseOsm(progressMonitor.createSubTaskMonitor(0, false)); - progressMonitor.worked((int) next.allPrimitives().stream().filter(IPrimitive::isTagged).count()); - returnDs.mergeFrom(next); - } - } catch (OsmTransferException e) { - throw new JosmRuntimeException(e); - } finally { - progressMonitor.finishTask(); - } - } catch (MalformedURLException e) { - throw new UncheckedIOException(e); - } - return returnDs; - } - - private static String getMapWithAISourceTag(MapWithAIInfo info) { - return info.getName() == null ? MapWithAIPlugin.NAME : info.getName(); - } - - /** - * Returns the name of the download task to be displayed in the - * {@link ProgressMonitor}. - * - * @return task name - */ - @Override - protected String getTaskName() { - return tr("Contacting {0} Server...", MapWithAIPlugin.NAME); - } - - @Override - protected String getBaseUrl() { - return url; - } - - @Override - protected void adaptRequest(HttpClient request) { - final var defaultUserAgent = new StringBuilder(); - request.setReadTimeout(DEFAULT_TIMEOUT); - defaultUserAgent.append(request.getHeaders().get("User-Agent")); - if (defaultUserAgent.toString().trim().length() == 0) { - defaultUserAgent.append("JOSM"); - } - defaultUserAgent.append(tr("/ {0} {1}", MapWithAIPlugin.NAME, MapWithAIPlugin.getVersionInfo())); - request.setHeader("User-Agent", defaultUserAgent.toString()); - } - - @Override - public void cancel() { - super.cancel(); - if (dcs != null) { - dcs.cancel(true); - } - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DataAvailability.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DataAvailability.java deleted file mode 100644 index 57ceb682..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DataAvailability.java +++ /dev/null @@ -1,276 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.TreeMap; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.coor.LatLon; -import org.openstreetmap.josm.io.CachedFile; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Territories; -import org.openstreetmap.josm.tools.Utils; - -import jakarta.json.Json; -import jakarta.json.JsonException; -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; - -/** - * Check for data availability in an area - */ -public class DataAvailability { - /** A map of tag -> message of possible data types */ - static final Map POSSIBLE_DATA_POINTS = new TreeMap<>(); - - private static final String PROVIDES = "provides"; - private static final int SEVEN_DAYS_IN_SECONDS = 604_800; - - /** - * Map<Source, - * Map<(url|parameters|countries|license|osm_compatible|permission_url), - * Object>> - */ - protected static final Map> COUNTRY_MAP = new HashMap<>(); - /** - * This holds classes that can give availability of data for a specific service - */ - private static final List> DATA_SOURCES = new ArrayList<>(); - - private static DataAvailability instance; - - /** - * A map of countries to a map of available types - * ({@code Map>} - */ - static final Map> COUNTRIES = new HashMap<>(); - - protected DataAvailability() { - if (DataAvailability.class.equals(this.getClass())) { - initialize(); - } - } - - /** - * Initialize the class - */ - private static void initialize() { - try (var jsonFile = new CachedFile(MapWithAIConfig.getUrls().getMapWithAISourcesJson()); - var jsonParser = Json.createParser(jsonFile.getContentReader())) { - jsonFile.setMaxAge(SEVEN_DAYS_IN_SECONDS); - jsonParser.next(); - final var jsonObject = jsonParser.getObject(); - for (var entry : jsonObject.entrySet()) { - Logging.debug("{0}: {1}", entry.getKey(), entry.getValue()); - if (JsonValue.ValueType.OBJECT == entry.getValue().getValueType() - && entry.getValue().asJsonObject().containsKey("countries")) { - final var countries = entry.getValue().asJsonObject().get("countries"); - parseCountries(COUNTRIES, countries, entry.getValue()); - } - } - } catch (JsonException | IOException e) { - Logging.debug(e); - } - - DATA_SOURCES.forEach(clazz -> { - try { - clazz.getConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | NoSuchMethodException | SecurityException e) { - Logging.debug(e); - } - }); - } - - /** - * Parse a JSON Value for country information - * - * @param countriesMap The countries map (will be modified) - * @param countries The country object (JsonObject) - * @param information The information for the source - */ - private static void parseCountries(Map> countriesMap, JsonValue countries, - JsonValue information) { - if (JsonValue.ValueType.OBJECT == countries.getValueType()) { - parseCountriesObject(countriesMap, countries.asJsonObject(), information); - } else { - Logging.error("MapWithAI: Check format of countries map from MapWithAI"); - } - } - - /** - * Parse a JsonObject for countries - * - * @param countriesMap The countries map (will be modified) - * @param countryObject The country object (JsonObject) - * @param information The information for the source - */ - private static void parseCountriesObject(Map> countriesMap, JsonObject countryObject, - JsonValue information) { - for (Map.Entry entry : countryObject.entrySet()) { - Map providesMap = countriesMap.getOrDefault(entry.getKey(), new TreeMap<>()); - countriesMap.putIfAbsent(entry.getKey(), providesMap); - if (JsonValue.ValueType.ARRAY == entry.getValue().getValueType()) { - for (String provide : entry.getValue().asJsonArray().stream() - .filter(c -> JsonValue.ValueType.STRING == c.getValueType()).map(JsonValue::toString) - .map(DataAvailability::stripQuotes).toList()) { - providesMap.put(provide, true); - } - } - if (providesMap.isEmpty() && JsonValue.ValueType.OBJECT == information.getValueType() - && information.asJsonObject().containsKey(PROVIDES) - && JsonValue.ValueType.ARRAY == information.asJsonObject().get(PROVIDES).getValueType()) { - for (String provide : information.asJsonObject().getJsonArray(PROVIDES).stream() - .filter(val -> JsonValue.ValueType.STRING == val.getValueType()).map(JsonValue::toString) - .map(DataAvailability::stripQuotes).toList()) { - providesMap.put(provide, Boolean.TRUE); - } - } - } - } - - /** - * Strip double quotes (") from a string - * - * @param string A string that may have quotes at the beginning, the end, or - * both - * @return A string that doesn't have quotes at the beginning or end - */ - public static String stripQuotes(String string) { - return string.replaceAll("((^\")|(\"$))", ""); - } - - /** - * Get the global instance that should be used to check for data availability - * - * @return the unique instance - */ - public static DataAvailability getInstance() { - if (instance == null || COUNTRIES.isEmpty() || MapWithAIPreferenceHelper.getMapWithAIUrl().isEmpty()) { - instance = new DataAvailability(); - } - return instance; - } - - /** - * Check if a bounds may have data - * - * @param bounds An area that may have data - * @return True if one of the corners of the {@code bounds} is in a country with - * available data. - */ - public boolean hasData(Bounds bounds) { - final List corners = new ArrayList<>(); - corners.add(bounds.getMin()); - corners.add(new LatLon(bounds.getMinLat(), bounds.getMaxLon())); - corners.add(bounds.getMax()); - corners.add(new LatLon(bounds.getMaxLat(), bounds.getMinLon())); - corners.add(bounds.getCenter()); - return corners.stream().anyMatch(this::hasData); - } - - /** - * Check if a latlon point may have data - * - * @param latLon A point that may have data from MapWithAI - * @return true if it is in an ares with data from MapWithAI - */ - public boolean hasData(LatLon latLon) { - for (final Map.Entry> entry : COUNTRIES.entrySet()) { - Logging.debug(entry.getKey()); - if (Territories.isIso3166Code(entry.getKey(), latLon)) { - return entry.getValue().entrySet().stream().anyMatch(Map.Entry::getValue); - } - } - return false; - } - - /** - * Get data types that may be visible around a point - * - * @param latLon The point of interest - * @return A map that may have available data types (or be empty) - */ - public static Map getDataTypes(LatLon latLon) { - return COUNTRIES.entrySet().stream().filter(entry -> Territories.isIso3166Code(entry.getKey(), latLon)) - .map(Map.Entry::getValue).findFirst().orElse(Collections.emptyMap()); - } - - /** - * Get the URL that this class is responsible for - * - * @return The url (e.g., example.com/addresses/{bbox}), or null if generic - */ - public String getUrl() { - return null; - } - - /** - * Get the Terms Of Use Url - * - * @return The url or "" - */ - public String getTermsOfUseUrl() { - return ""; - } - - /** - * Get the Privacy Policy Url - * - * @return The url or the TOS url (sometimes a privacy policy is in the TOS). - * The TOS url may be an empty string. - */ - public String getPrivacyPolicyUrl() { - return getTermsOfUseUrl(); - } - - /** - * Get the terms of use for all specially handled URL's - * - * @return List of terms of use urls - */ - public static List getTermsOfUse() { - return Stream.concat(MapWithAILayerInfo.getInstance().getLayers().stream().map(MapWithAIInfo::getTermsOfUseURL), - DATA_SOURCES.stream().map(clazz -> { - try { - return clazz.getConstructor().newInstance().getTermsOfUseUrl(); - } catch (ReflectiveOperationException e) { - Logging.debug(e); - } - return ""; - })).filter(Objects::nonNull).filter(str -> !Utils.removeWhiteSpaces(str).isEmpty()) - .collect(Collectors.toList()); - } - - /** - * Get the privacy policy for all specially handled URL's - * - * @return List of privacy policy urls - */ - public static List getPrivacyPolicy() { - return Stream - .concat(MapWithAILayerInfo.getInstance().getLayers().stream().map(MapWithAIInfo::getPrivacyPolicyURL), - DATA_SOURCES.stream().map(clazz -> { - try { - return clazz.getConstructor().newInstance().getPrivacyPolicyUrl(); - } catch (ReflectiveOperationException e) { - Logging.debug(e); - } - return ""; - })) - .filter(Objects::nonNull).filter(str -> !Utils.removeWhiteSpaces(str).isEmpty()).distinct() - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DataConflationSender.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DataConflationSender.java deleted file mode 100644 index 0ac424e5..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DataConflationSender.java +++ /dev/null @@ -1,186 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.net.URI; -import java.net.URLEncoder; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.http.HttpTimeoutException; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.Map; -import java.util.Objects; -import java.util.TreeMap; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.RunnableFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.gui.progress.NullProgressMonitor; -import org.openstreetmap.josm.io.IllegalDataException; -import org.openstreetmap.josm.io.NetworkManager; -import org.openstreetmap.josm.io.OsmReader; -import org.openstreetmap.josm.io.OsmWriterFactory; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIConflationCategory; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Utils; - -/** - * Conflate data with a third party server - * - * @author Taylor Smock - */ -public class DataConflationSender implements RunnableFuture { - - private static final int MAX_POLLS = 100; - private final DataSet external; - private final DataSet osm; - private final MapWithAICategory category; - private DataSet conflatedData; - private HttpClient client; - private boolean done; - private boolean cancelled; - - /** - * Conflate external data - * - * @param category The category to use to determine the conflation server - * @param openstreetmap The OSM data (may be null -- try to avoid this) - * @param external The data to conflate (may not be null) - */ - public DataConflationSender(MapWithAICategory category, DataSet openstreetmap, DataSet external) { - Objects.requireNonNull(external, tr("We must have data to conflate")); - Objects.requireNonNull(category, tr("We must have a category for the data")); - this.osm = openstreetmap; - this.external = external; - this.category = category; - } - - @Override - public void run() { - String url = MapWithAIConflationCategory.conflationUrlFor(category); - if (!Utils.isStripEmpty(url) && !NetworkManager.isOffline(url)) { - this.client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); - try { - final var form = new TreeMap(); - if (osm != null) { - final var output = new StringWriter(); - try (var writer = OsmWriterFactory.createOsmWriter(new PrintWriter(output), true, "0.6")) { - writer.write(osm); - form.put("openstreetmap", output.toString()); // APPLICATION_XML - } - } - // We need to reset the writers to avoid writing previous streams - final var output = new StringWriter(); - try (var writer = OsmWriterFactory.createOsmWriter(new PrintWriter(output), true, "0.6")) { - writer.write(external); - form.put("external", output.toString()); - } - - final var boundary = UUID.randomUUID().toString(); - final var request = HttpRequest.newBuilder(URI.create(url)) - .POST(HttpRequest.BodyPublishers.ofString(formEncodeMap(boundary, form))) - .timeout(Duration.ofSeconds(10)) - .header("Content-Type", "multipart/form-data;boundary=" + boundary).build(); - final var response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()); - if (response.statusCode() == 200) { - conflatedData = OsmReader.parseDataSet(response.body(), NullProgressMonitor.INSTANCE, - OsmReader.Options.SAVE_ORIGINAL_ID); - } else { - conflatedData = null; - } - Logging.info(request.method() + ' ' + url + " -> " + request.uri().getScheme() + '/' - + response.version() + ' ' + response.statusCode()); - } catch (HttpTimeoutException httpTimeoutException) { - final var oldThrowable = NetworkManager.addNetworkError(url, httpTimeoutException); - if (oldThrowable != null) { - Logging.trace(oldThrowable); - } - } catch (IOException | UnsupportedOperationException | IllegalDataException e) { - Logging.error(e); - } catch (InterruptedException e) { - Logging.trace(e); - if (!this.isCancelled()) { - this.cancel(false); - } - Thread.currentThread().interrupt(); - } - } - this.done = true; - synchronized (this) { - this.notifyAll(); - } - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - this.done = true; - this.cancelled = true; - if (this.client != null) { - this.client.executor().ifPresent(Object::notifyAll); - } - this.client = null; - synchronized (this) { - this.notifyAll(); - } - return true; - } - - @Override - public boolean isCancelled() { - return this.cancelled; - } - - @Override - public boolean isDone() { - return this.done; - } - - @Override - public DataSet get() throws InterruptedException, ExecutionException { - synchronized (this) { - while (!isDone()) { - this.wait(100); - } - } - return this.conflatedData; - } - - @Override - public DataSet get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - long realtime = unit.toMillis(timeout); - long waitTime = realtime > MAX_POLLS ? realtime / MAX_POLLS : 1; - long timeWaited = 0; - synchronized (this) { - while (!isDone()) { - this.wait(waitTime); - timeWaited += waitTime; - } - } - if (!isDone() && timeWaited > realtime) { - throw new TimeoutException(); - } - return this.conflatedData; - } - - private static String formEncodeMap(String boundary, Map form) { - final var separator = "--" + boundary + "\r\nContent-Disposition: form-data; name="; - final var builder = new StringBuilder(); - for (var entry : form.entrySet()) { - builder.append(separator).append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8)) - .append(";\r\nContent-Type: application/xml\r\n\r\n") - .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).append("\r\n"); - } - builder.append("--").append(boundary).append("--"); - return builder.toString(); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DetectTaskingManagerUtils.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DetectTaskingManagerUtils.java deleted file mode 100644 index 41c35c2e..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DetectTaskingManagerUtils.java +++ /dev/null @@ -1,94 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.time.Instant; -import java.util.regex.Pattern; -import java.util.stream.Stream; - -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.coor.LatLon; -import org.openstreetmap.josm.data.gpx.GpxData; -import org.openstreetmap.josm.data.gpx.GpxRoute; -import org.openstreetmap.josm.data.gpx.WayPoint; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.layer.GpxLayer; -import org.openstreetmap.josm.gui.layer.Layer; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; - -/** - * Various methods to simplify detection of tasking manager tasks - * - * @author Taylor Smock - * - */ -final class DetectTaskingManagerUtils { - public static final String MAPWITHAI_CROP_AREA = tr("{0}: Crop Area", MapWithAIPlugin.NAME); - private static final Pattern[] PATTERNS = { Pattern.compile("^Task Boundaries.*$"), - Pattern.compile("^" + MAPWITHAI_CROP_AREA + "$"), Pattern.compile("^Boundary for task:.*$") }; - - private DetectTaskingManagerUtils() { - // Hide since this is going to be a static class - } - - /** - * Check for a tasking manager layer - * - * @return True if there is a tasking manager layer - */ - public static boolean hasTaskingManagerLayer() { - return getTaskingManagerLayer() != null; - } - - /** - * Get a tasking manager layer - * - * @return A {@link Layer} that matches a pattern defined in - * {@link DetectTaskingManagerUtils#PATTERNS} or {@code null}. - */ - public static Layer getTaskingManagerLayer() { - return MainApplication.getLayerManager().getLayers().stream().filter(tlayer -> Stream.of(PATTERNS).parallel() - .anyMatch(pattern -> pattern.matcher(tlayer.getName()).matches())).findFirst().orElse(null); - } - - /** - * Get the bounds from the tasking manager layer - * - * @return A {@link Bounds} made from a tasking manager layer, or one that is - * not valid. - */ - public static Bounds getTaskingManagerBounds() { - final var layer = getTaskingManagerLayer(); - if (layer instanceof GpxLayer gpxLayer) { - final var realBounds = gpxLayer.data.recalculateBounds(); - return new Bounds(realBounds); - } else if (layer instanceof OsmDataLayer osmDataLayer && osmDataLayer.getDataSet().getWays().size() == 1) { - final var bbox = osmDataLayer.getDataSet().getWays().iterator().next().getBBox(); - final var returnBounds = new Bounds(bbox.getTopLeft()); - returnBounds.extend(bbox.getBottomRight()); - return returnBounds; - } - return new Bounds(0, 0, 0, 0); - } - - /** - * Create a GpxData that can be used to define a crop area - * - * @param bounds A bounds to crop data to - * @return A gpx layer that can be used to crop data from MapWithAI - */ - public static GpxData createTaskingManagerGpxData(Bounds bounds) { - final var data = new GpxData(); - final var route = new GpxRoute(); - route.routePoints.add(new WayPoint(bounds.getMin())); - route.routePoints.add(new WayPoint(new LatLon(bounds.getMaxLat(), bounds.getMinLon()))); - route.routePoints.add(new WayPoint(bounds.getMax())); - route.routePoints.add(new WayPoint(new LatLon(bounds.getMinLat(), bounds.getMaxLon()))); - route.routePoints.add(route.routePoints.iterator().next()); - route.routePoints.forEach(waypoint -> waypoint.setInstant(Instant.EPOCH)); - data.addRoute(route); - return data; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DownloadListener.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DownloadListener.java deleted file mode 100644 index 3cf88cb9..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DownloadListener.java +++ /dev/null @@ -1,76 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import java.lang.ref.WeakReference; -import java.util.Collection; -import java.util.HashSet; -import java.util.Objects; - -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.DataSource; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.DataSourceChangeEvent; -import org.openstreetmap.josm.data.osm.DataSourceListener; -import org.openstreetmap.josm.data.osm.event.DataSourceAddedEvent; -import org.openstreetmap.josm.tools.Destroyable; - -/** - * This class listens for download events, and then gets new data - * - * @author Taylor Smock - * - */ -public final class DownloadListener implements DataSourceListener, Destroyable { - - final WeakReference ds; - private static final double BBOX_SIMILARITY_DEGREES = 0.001; - private static final Collection LISTENERS = new HashSet<>(); - - /** - * Listen to downloads in a dataset - * - * @param dataSet The dataset to listen to - */ - public DownloadListener(DataSet dataSet) { - Objects.requireNonNull(dataSet, "DataSet cannot be null"); - ds = new WeakReference<>(dataSet); - dataSet.addDataSourceListener(this); - LISTENERS.add(this); - } - - @Override - public void dataSourceChange(DataSourceChangeEvent event) { - if (event instanceof DataSourceAddedEvent) { - final var layer = MapWithAIDataUtils.getLayer(false); - if (layer == null) { - destroy(); - return; - } - if (layer.downloadContinuous()) { - final var bounds = DataSource.getDataSourceBounds(event.getSource().getDataSources()); - bounds.removeIf(a -> layer.getDataSet().getDataSourceBounds().stream().map(Bounds::toBBox) - .anyMatch(b -> b.bboxIsFunctionallyEqual(a.toBBox(), BBOX_SIMILARITY_DEGREES))); - MapWithAIDataUtils.getMapWithAIData(layer, bounds); - - } - } - } - - @Override - public void destroy() { - final var realDs = ds.get(); - if (realDs != null) { - // Should be added, so no exception should be thrown - realDs.removeDataSourceListener(this); - ds.clear(); - LISTENERS.remove(this); - } - } - - /** - * Destroy all download listeners for MapWithAI - */ - public static void destroyAll() { - new HashSet<>(LISTENERS).forEach(DownloadListener::destroy); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DownloadMapWithAITask.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DownloadMapWithAITask.java deleted file mode 100644 index ce023149..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DownloadMapWithAITask.java +++ /dev/null @@ -1,201 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ForkJoinTask; -import java.util.concurrent.Future; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask; -import org.openstreetmap.josm.actions.downloadtasks.DownloadParams; -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.Notification; -import org.openstreetmap.josm.gui.progress.ProgressMonitor; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.io.OsmApiException; -import org.openstreetmap.josm.io.OsmServerReader; -import org.openstreetmap.josm.io.OsmTransferException; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.tools.ImageProvider; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Utils; -import org.xml.sax.SAXException; - -/** - * Download data from MapWithAI - */ -public class DownloadMapWithAITask extends DownloadOsmTask { - private final List urls; - - /** - * Show common notifications/issues - */ - private static final class Notifications { - private Notifications() { - // Hide constructor - } - - /** - * Show a notification that no data sources that are enabled have data in the - * area. - */ - public static void showEmptyNotification() { - GuiHelper.runInEDT(Notifications::realShowEmptyNotification); - } - - private static void realShowEmptyNotification() { - final var n = new Notification(); - n.setIcon(ImageProvider.get("mapwithai")); - n.setContent(tr("No enabled data sources have potential data in this area")); - n.show(); - } - - /** - * Show a notification that no MapWithAI layers are enabled. - */ - public static void showNoLayerNotification() { - GuiHelper.runInEDT(Notifications::realShowNoLayerNotification); - } - - private static void realShowNoLayerNotification() { - final var n = new Notification(); - n.setIcon(ImageProvider.get("mapwithai")); - n.setContent(tr("No MapWithAI layers were selected. Please select at least one.")); - GuiHelper.runInEDT(n::show); - } - } - - /** - * Create a new download task - */ - public DownloadMapWithAITask() { - urls = MapWithAILayerInfo.getInstance().getLayers(); - MapWithAILayerInfo.getInstance().save(); // Save preferences between downloads. - } - - @Override - public Future download(OsmServerReader reader, DownloadParams settings, Bounds downloadArea, - ProgressMonitor progressMonitor) { - if (!urls.isEmpty()) { - final var task = new DownloadTask(settings, tr("MapWithAI Download"), progressMonitor, false, false, - downloadArea); - return MainApplication.worker.submit(task); - } - Notifications.showNoLayerNotification(); - return CompletableFuture.completedFuture(null); - } - - @Override - public String getConfirmationMessage(URL url) { - if (url != null) { - final var items = new ArrayList<>(); - items.add(tr("OSM Server URL:") + ' ' + url.getHost()); - items.add(tr("Command") + ": " + url.getPath()); - if (url.getQuery() != null) { - items.add(tr("Request details: {0}", url.getQuery().replaceAll(",\\s*", ", "))); - } - return Utils.joinAsHtmlUnorderedList(items); - } - return null; - } - - class DownloadTask extends AbstractInternalTask { - List> downloader; - final Bounds bounds; - private List relevantUrls; - - public DownloadTask(DownloadParams settings, String title, ProgressMonitor progressMonitor, - boolean ignoreException, boolean zoomAfterDownload, Bounds bounds) { - super(settings, title, progressMonitor, ignoreException, zoomAfterDownload); - this.bounds = bounds; - } - - @Override - protected void cancel() { - setCanceled(true); - if (downloader != null) { - downloader.forEach(task -> task.cancel(true)); - } - } - - @Override - protected void realRun() throws SAXException, IOException, OsmTransferException { - relevantUrls = urls.stream().filter(i -> i.getBounds() == null || i.getBounds().intersects(bounds)) - .collect(Collectors.toList()); - final var monitor = getProgressMonitor(); - if (relevantUrls.isEmpty()) { - Notifications.showEmptyNotification(); - return; - } - if (relevantUrls.size() < 5) { - monitor.indeterminateSubTask(tr("MapWithAI Download")); - } else { - monitor.setTicksCount(relevantUrls.size()); - } - downloadedData = new DataSet(); - final var pool = MapWithAIDataUtils.getForkJoinPool(); - this.downloader = new ArrayList<>(relevantUrls.size()); - for (MapWithAIInfo info : relevantUrls) { - if (isCanceled()) { - break; - } - this.downloader.add(pool.submit(MapWithAIDataUtils.download(this.progressMonitor, bounds, info, - MapWithAIDataUtils.MAXIMUM_SIDE_DIMENSIONS))); - } - for (var task : this.downloader) { - try { - DownloadMapWithAITask.this.downloadedData.mergeFrom(task.get(), - monitor.createSubTaskMonitor(1, false)); - } catch (CancellationException e) { - Logging.trace(e); - return; - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - this.downloader.forEach(t -> t.cancel(true)); - throw new IOException(e); - } catch (ExecutionException e) { - // Throw the "original" exception type, if at all possible. - Throwable current = e; - while (current.getCause() != null && !current.equals(current.getCause())) { - current = current.getCause(); - } - if (current instanceof OsmApiException ex) { - final var here = new OsmApiException(ex.getResponseCode(), ex.getErrorHeader(), - ex.getErrorBody(), ex.getAccessedUrl(), ex.getLogin(), ex.getContentType()); - here.initCause(e); - here.setUrl(here.getAccessedUrl()); - throw here; - } - if (current instanceof OsmTransferException) { - throw new OsmTransferException(current.getMessage(), e); - } - throw new IOException(e); - } - } - } - - @Override - protected void finish() { - if (!isCanceled() && !isFailed()) { - synchronized (DownloadMapWithAITask.DownloadTask.class) { - MapWithAILayer layer = MapWithAIDataUtils.getLayer(true); - layer.getDataSet().mergeFrom(downloadedData); - relevantUrls.forEach(layer::addDownloadedInfo); - } - GetDataRunnable.cleanup(MapWithAIDataUtils.getLayer(true).getDataSet(), null, null); - } - } - - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/GetDataRunnable.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/GetDataRunnable.java deleted file mode 100644 index 697b2cd2..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/GetDataRunnable.java +++ /dev/null @@ -1,746 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static java.util.function.Predicate.not; -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.io.Serial; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.TreeMap; -import java.util.concurrent.ForkJoinTask; -import java.util.concurrent.RecursiveTask; -import java.util.function.BiPredicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.openstreetmap.josm.actions.MergeNodesAction; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.DeleteCommand; -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.coor.ILatLon; -import org.openstreetmap.josm.data.coor.LatLon; -import org.openstreetmap.josm.data.osm.AbstractPrimitive; -import org.openstreetmap.josm.data.osm.BBox; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.Hash; -import org.openstreetmap.josm.data.osm.INode; -import org.openstreetmap.josm.data.osm.IPrimitive; -import org.openstreetmap.josm.data.osm.IRelation; -import org.openstreetmap.josm.data.osm.IWay; -import org.openstreetmap.josm.data.osm.IWaySegment; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Storage; -import org.openstreetmap.josm.data.osm.Tag; -import org.openstreetmap.josm.data.osm.TagMap; -import org.openstreetmap.josm.data.osm.UploadPolicy; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor; -import org.openstreetmap.josm.data.projection.ProjectionRegistry; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.gui.progress.NullProgressMonitor; -import org.openstreetmap.josm.gui.progress.ProgressMonitor; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.commands.MergeDuplicateWays; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.PreConflatedDataUtils; -import org.openstreetmap.josm.tools.Geometry; -import org.openstreetmap.josm.tools.JosmRuntimeException; -import org.openstreetmap.josm.tools.Pair; -import org.openstreetmap.josm.tools.Utils; - -/** - * Get data in a parallel manner - * - * @author Taylor Smock - */ -public class GetDataRunnable extends RecursiveTask { - /** - * This is functionally equivalent to - * {@link org.openstreetmap.josm.data.validation.tests.DuplicateNode.NodeHash} - */ - private static class ILatLonHash implements Hash { - private static final double PRECISION = DEGREE_BUFFER; - - /** - * Returns the rounded coordinated according to {@link #PRECISION} - * - * @param coor The coordinate to round - * @return The rounded coordinate - * @see LatLon#roundToOsmPrecision - */ - private static ILatLon roundCoord(ILatLon coor) { - return new LatLon(Math.round(coor.lat() / PRECISION) * PRECISION, - Math.round(coor.lon() / PRECISION) * PRECISION); - } - - @SuppressWarnings("unchecked") - protected static ILatLon getLatLon(Object o) { - if (o instanceof INode) { - LatLon coor = ((INode) o).getCoor(); - if (coor == null) - return null; - if (PRECISION == 0) - return coor.getRoundedToOsmPrecision(); - return roundCoord(coor); - } else if (o instanceof List) { - LatLon coor = ((List) o).get(0).getCoor(); - if (coor == null) - return null; - if (PRECISION == 0) - return coor.getRoundedToOsmPrecision(); - return roundCoord(coor); - } else - throw new AssertionError(); - } - - @Override - public boolean equals(Object k, Object t) { - ILatLon coorK = getLatLon(k); - ILatLon coorT = getLatLon(t); - return Objects.equals(coorK, coorT); - } - - @Override - public int getHashCode(Object k) { - ILatLon coorK = getLatLon(k); - return coorK == null ? 0 : coorK.hashCode(); - } - } - - /** - * This checks that all visited objects are highways - */ - private static final class HighwayVisitor implements PrimitiveVisitor { - boolean onlyHighways = true; - - @Override - public void visit(INode n) { - onlyHighways = false; - } - - @Override - public void visit(IWay w) { - if (!w.isTagged() || !w.hasTag("highway")) { - onlyHighways = false; - } - } - - @Override - public void visit(IRelation r) { - onlyHighways = false; - } - } - - @Serial - private static final long serialVersionUID = 258423685658089715L; - private final transient List runnableBounds; - private final transient DataSet dataSet; - private final transient ProgressMonitor monitor; - private static final float DEGREE_BUFFER = 0.001f; - private static final int MAX_LATITUDE = 90; - private static final int MAX_LONGITUDE = 180; - - private Integer maximumDimensions; - private transient MapWithAIInfo info; - - private static final int MAX_NUMBER_OF_BBOXES_TO_PROCESS = 1; - private static final String SERVER_ID_KEY = "current_id"; - - /** An equals sign (=) used for tag splitting */ - private static final String EQUALS = "="; - - private static final double ARTIFACT_ANGLE = 0.1745; // 10 degrees in radians - - /** - * The source tag to be used to populate source values. Not seen on objects - * post-upload. - */ - public static final String MAPWITHAI_SOURCE_TAG_KEY = "mapwithai:source"; - /** - * The source tag to be added to all objects from the source. Seen on objects - * post-upload. - */ - public static final String SOURCE_TAG_KEY = "source"; - - /** - * Get data in the background - * - * @param bbox The initial bbox to get data from (don't reduce beforehand -- - * it will be reduced here) - * @param dataSet The dataset to add the data to - * @param monitor A monitor to keep track of progress - */ - public GetDataRunnable(Bounds bbox, DataSet dataSet, ProgressMonitor monitor) { - this(Collections.singletonList(bbox), dataSet, monitor); - } - - /** - * Get data in the background - * - * @param bbox The initial bboxes to get data from (don't reduce beforehand - * -- it will be reduced here) - * @param dataSet The dataset to add the data to - * @param monitor A monitor to keep track of progress - */ - public GetDataRunnable(List bbox, DataSet dataSet, ProgressMonitor monitor) { - super(); - this.runnableBounds = bbox.stream().distinct().collect(Collectors.toList()); - this.dataSet = dataSet; - this.monitor = Optional.ofNullable(monitor).orElse(NullProgressMonitor.INSTANCE); - } - - /** - * Set the maximum download bbox size. Must be called before execution. - * - * @param maximumDimensions The maximum bbox download size - */ - public void setMaximumDimensions(int maximumDimensions) { - this.maximumDimensions = maximumDimensions; - } - - @Override - public DataSet compute() { - final var bounds = maximumDimensions == null ? MapWithAIDataUtils.reduceBoundSize(runnableBounds) - : MapWithAIDataUtils.reduceBoundSize(runnableBounds, maximumDimensions); - monitor.beginTask(tr("Downloading {0} data ({1} total downloads)", MapWithAIPlugin.NAME, bounds.size()), - bounds.size() - 1); - if (!monitor.isCanceled()) { - if (bounds.size() == MAX_NUMBER_OF_BBOXES_TO_PROCESS) { - final var temporaryDataSet = getDataReal(bounds.get(0), monitor); - this.dataSet.update(() -> dataSet.mergeFrom(temporaryDataSet)); - } else { - final Collection tasks = bounds.stream() - .map(bound -> new GetDataRunnable(bound, dataSet, monitor.createSubTaskMonitor(0, true))) - .toList(); - tasks.forEach(GetDataRunnable::fork); - tasks.forEach(runnable -> { - runnable.join(); - monitor.worked(1); - }); - } - } - // This can technically be included in the above block, but it is here so that - // cancellation is a little faster - if (!monitor.isCanceled() && !bounds.isEmpty()) { - cleanup(dataSet, bounds.get(0), info); - } - monitor.finishTask(); - return dataSet; - } - - /** - * Perform cleanups on a dataset (one dataset at a time) - * - * @param dataSet The dataset to cleanup - * @param bounds The newly added bounds to the dataset. May be {@code null}. - * @param info The information used to download the data - */ - public static void cleanup(DataSet dataSet, Bounds bounds, MapWithAIInfo info) { - dataSet.update(() -> realCleanup(dataSet, bounds, info)); - } - - private static void realCleanup(DataSet dataSet, Bounds bounds, MapWithAIInfo info) { - final Bounds boundsToUse; - if (bounds == null && !dataSet.getDataSourceBounds().isEmpty()) { - boundsToUse = new Bounds(dataSet.getDataSourceBounds().get(0)); - dataSet.getDataSourceBounds().forEach(boundsToUse::extend); - } else if (bounds == null) { - boundsToUse = new Bounds(0, 0, 0, 0); - } else { - boundsToUse = new Bounds(bounds); - } - replaceTags(dataSet); - removeCommonTags(dataSet); - removeEmptyTags(dataSet, bounds); - mergeNodes(dataSet); - cleanupDataSet(dataSet); - mergeWays(dataSet); - PreConflatedDataUtils.removeConflatedData(dataSet, info); - removeAlreadyAddedData(dataSet); - final var ways = dataSet.searchWays(boundsToUse.toBBox()).stream().filter(w -> w.hasKey("highway")).toList(); - if (!ways.isEmpty()) { - new MergeDuplicateWays(dataSet, ways).executeCommand(); - } - (boundsToUse.isCollapsed() || boundsToUse.isOutOfTheWorld() ? dataSet.getWays() - : dataSet.searchWays(boundsToUse.toBBox())).stream().filter(way -> !way.isDeleted()) - .forEach(GetDataRunnable::cleanupArtifacts); - } - - /** - * Remove empty tags from primitives - * - * @param dataSet The dataset to remove tags from - * @param bounds The bounds to remove the empty tags from (performance) - */ - public static void removeEmptyTags(DataSet dataSet, Bounds bounds) { - Bounds boundsToUse; - if (bounds == null && !dataSet.getDataSourceBounds().isEmpty()) { - boundsToUse = new Bounds(dataSet.getDataSourceBounds().get(0)); - dataSet.getDataSourceBounds().forEach(boundsToUse::extend); - } else if (bounds == null) { - boundsToUse = new Bounds(-MAX_LATITUDE, -MAX_LONGITUDE, MAX_LATITUDE, MAX_LONGITUDE); - } else { - boundsToUse = new Bounds(bounds); - } - dataSet.searchPrimitives(boundsToUse.toBBox()).forEach(GetDataRunnable::realRemoveEmptyTags); - } - - /** - * Remove empty tags from primitives. We assume that the `getKey` implementation - * returns a new map. - * - * @param prim The primitive to remove empty tags from. - */ - private static void realRemoveEmptyTags(IPrimitive prim) { - final var keys = prim.getKeys(); - for (var entry : keys.entrySet()) { - if (entry.getValue() == null || entry.getValue().trim().isEmpty()) { - // The OsmPrimitives all return a new map, so this is safe. - prim.remove(entry.getKey()); - } - } - } - - /** - * Remove ways that have already been added to an OSM layer - * - * @param dataSet The dataset with potential duplicate ways (it is modified) - */ - public static void removeAlreadyAddedData(DataSet dataSet) { - final var osmData = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream() - .map(OsmDataLayer::getDataSet).filter(ds -> !ds.equals(dataSet)).toList(); - for (var way : dataSet.getWays()) { - if (!way.isDeleted() && way.getOsmId() <= 0) { - for (var ds : osmData) { - if (checkIfPrimitiveDuplicatesPrimitiveInDataSet(way, ds)) { - final var nodes = way.getNodes(); - Optional.ofNullable(DeleteCommand.delete(Collections.singleton(way), true, true)) - .ifPresent(Command::executeCommand); - for (var node : nodes) { - if (!node.isDeleted() - && node.referrers(OsmPrimitive.class).allMatch(OsmPrimitive::isDeleted)) { - node.setDeleted(true); - } - } - break; - } - } - } - } - } - - private static boolean checkIfPrimitiveDuplicatesPrimitiveInDataSet(OsmPrimitive primitive, DataSet ds) { - final var possibleDuplicates = searchDataSet(ds, primitive); - for (var dupe : possibleDuplicates) { - if (checkIfProbableDuplicate(dupe, primitive)) { - return true; - } - } - return false; - } - - private static boolean checkIfProbableDuplicate(OsmPrimitive one, OsmPrimitive two) { - var equivalent = false; - final TagMap oneMap = one.getKeys(); - final TagMap twoMap = two.getKeys(); - oneMap.remove(MAPWITHAI_SOURCE_TAG_KEY); - twoMap.remove(MAPWITHAI_SOURCE_TAG_KEY); - if (one.getClass().equals(two.getClass()) && oneMap.equals(twoMap)) { - if (one instanceof Node coor1) { - final ILatLon coor2 = ((Node) two); - if (one.hasSameInterestingTags(two) && coor1.isLatLonKnown() && coor2.isLatLonKnown() - && coor1.equalsEpsilon(coor2)) { - equivalent = true; - } - } else if (one instanceof Way wayOne) { - equivalent = wayOne.getNodes().stream().filter(Objects::nonNull) - .allMatch(node1 -> ((Way) two).getNodes().stream().anyMatch(node1::equalsEpsilon)); - } else if (one instanceof Relation oneRelation) { - equivalent = oneRelation.getMembers().stream() - .allMatch(member1 -> ((Relation) two).getMembers().stream() - .anyMatch(member2 -> member1.getRole().equals(member2.getRole()) - && checkIfProbableDuplicate(member1.getMember(), member2.getMember()))); - } - } - return equivalent; - } - - private static List searchDataSet(DataSet ds, T primitive) { - if (primitive instanceof OsmPrimitive osmPrimitive) { - final var tBBox = new BBox(); - tBBox.addPrimitive(osmPrimitive, DEGREE_BUFFER); - if (primitive instanceof Node) { - return Collections.unmodifiableList(ds.searchNodes(tBBox)); - } else if (primitive instanceof Way) { - return Collections.unmodifiableList(ds.searchWays(tBBox)); - } else if (primitive instanceof Relation) { - return Collections.unmodifiableList(ds.searchRelations(tBBox)); - } - } - return Collections.emptyList(); - } - - /** - * Replace tags in a dataset with a set of replacement tags - * - * @param dataSet The dataset with primitives to change - */ - public static void replaceTags(DataSet dataSet) { - final var replaceTags = MapWithAIPreferenceHelper.getReplacementTags().entrySet().stream() - .filter(entry -> entry.getKey().contains(EQUALS) && entry.getValue().contains(EQUALS)) - .map(entry -> new Pair<>(Tag.ofString(entry.getKey()), Tag.ofString(entry.getValue()))) - .collect(Collectors.toMap(pair -> pair.a, pair -> pair.b)); - MapWithAIPreferenceHelper.getReplacementTags().entrySet().stream() - .filter(entry -> !entry.getKey().equals(EQUALS) && Utils.isStripEmpty(entry.getValue())) - .map(entry -> new Tag(entry.getKey(), null)).forEach(tag -> replaceTags.put(tag, tag)); - replaceTags(dataSet, replaceTags); - } - - /** - * Replace tags in a dataset with a set of replacement tags - * - * @param dataSet The dataset with primitives to change - * @param replaceTags The tags to replace - */ - public static void replaceTags(DataSet dataSet, Map replaceTags) { - replaceTags.forEach((orig, replace) -> dataSet.allNonDeletedPrimitives().stream() - .filter(prim -> prim.hasTag(orig.getKey(), orig.getValue()) - || (prim.hasKey(orig.getKey()) && Utils.isStripEmpty(orig.getValue()))) - .forEach(prim -> prim.put(replace))); - } - - /** - * Replace tags in a dataset with a set of replacement tags - * - * @param dataSet The dataset with primitives to change - * @param replaceKeys The keys to replace (does not replace values) - */ - public static void replaceKeys(DataSet dataSet, Map replaceKeys) { - replaceKeys.entrySet().stream().filter(e -> !e.getKey().equals(e.getValue())).forEach( - e -> dataSet.allNonDeletedPrimitives().stream().filter(p -> p.hasKey(e.getKey())).forEach(p -> { - p.put(e.getValue(), p.get(e.getKey())); - p.remove(e.getKey()); - })); - } - - private static void cleanupDataSet(DataSet dataSet) { - var origIds = dataSet.allPrimitives().stream().filter(prim -> prim.hasKey(MergeDuplicateWays.ORIG_ID)) - .distinct().collect(Collectors.toMap(prim -> prim, prim -> prim.get(MergeDuplicateWays.ORIG_ID))); - final var serverIds = dataSet.allPrimitives().stream().filter(prim -> prim.hasKey(SERVER_ID_KEY)).distinct() - .collect(Collectors.toMap(prim -> prim, prim -> prim.get(SERVER_ID_KEY))); - - final var toDelete = origIds.entrySet().stream().filter(entry -> serverIds.containsValue(entry.getValue())) - .map(Map.Entry::getKey).collect(Collectors.toList()); - if (!toDelete.isEmpty()) { - new DeleteCommand(toDelete).executeCommand(); - } - origIds = origIds.entrySet().stream().filter(entry -> !toDelete.contains(entry.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - serverIds.forEach((prim, str) -> prim.remove(SERVER_ID_KEY)); - origIds.forEach((prim, str) -> prim.remove(MergeDuplicateWays.ORIG_ID)); - } - - /** - * Remove common tags from the dataset - * - * @param dataSet The dataset to remove tags from - */ - public static void removeCommonTags(DataSet dataSet) { - final var emptyNodes = new HashSet(); - for (var tagged : dataSet.allPrimitives()) { - if (!tagged.hasKeys()) { - continue; - } - if (tagged.hasKey(MergeDuplicateWays.ORIG_ID)) { - tagged.remove(MergeDuplicateWays.ORIG_ID); - } - if (tagged instanceof Node node) { - tagged.remove(SERVER_ID_KEY); - if (!node.isDeleted() && !node.hasKeys() && node.getReferrers().isEmpty()) { - emptyNodes.add(node); - } - } - } - if (!emptyNodes.isEmpty()) { - new DeleteCommand(emptyNodes).executeCommand(); - } - } - - /** - * Create an efficient collection ({@link Storage}) of {@link List} of - * {@link Node}s and {@link Node} objects - * - * @param dataSet The dataset to get nodes from - * @return The storage to use - */ - private static Storage generateEfficientNodeSearchStorage(DataSet dataSet) { - final Storage nodes = new Storage<>(new ILatLonHash()); - for (Node node : dataSet.getNodes()) { - if (!node.isDeleted()) { - final Object old = nodes.get(node); - if (old == null) { - nodes.put(node); - } else if (old instanceof Node oldNode) { - List list = new ArrayList<>(2); - list.add(oldNode); - list.add(node); - nodes.put(list); - } else { - @SuppressWarnings("unchecked") - List list = (List) old; - list.add(node); - } - } - } - return nodes; - } - - /** - * Merge nodes that have the same tags and (almost) the same location - * - * @param dataSet The dataset to merge nodes in - */ - private static void mergeNodes(DataSet dataSet) { - final var nodes = generateEfficientNodeSearchStorage(dataSet); - for (var obj : nodes) { - // We only care if there are multiple nodes at the location - if (obj instanceof List) { - @SuppressWarnings("unchecked") - final var iNodes = (List) obj; - for (var nearNode : iNodes) { - final var nearbyNodes = new ArrayList<>(iNodes); - nearbyNodes.removeIf(node -> !nearNode.hasSameInterestingTags(node) || !usableNode(nearNode, node)); - final var mergeCommand = MergeNodesAction.mergeNodes(nearbyNodes, nearNode); - if (mergeCommand != null) { - mergeCommand.executeCommand(); - } - } - } - } - } - - private static boolean usableNode(Node nearNode, Node node) { - return basicNodeChecks(nearNode, node) && onlyHasHighwayParents(node) - && ((keyCheck(nearNode, node) - && distanceCheck(nearNode, node, MapWithAIPreferenceHelper.getMaxNodeDistance())) - || (nearNode.hasKeys() && node.hasKeys() && nearNode.getKeys().equals(node.getKeys()) - && distanceCheck(nearNode, node, MapWithAIPreferenceHelper.getMaxNodeDistance() * 10))); - } - - private static boolean distanceCheck(Node nearNode, Node node, Double distance) { - return Geometry.getDistance(nearNode, node) < distance; - } - - private static boolean keyCheck(INode nearNode, INode node) { - return !nearNode.hasKeys() || !node.hasKeys() || nearNode.getKeys().equals(node.getKeys()); - } - - private static boolean onlyHasHighwayParents(Node node) { - final var highwayVisitor = new HighwayVisitor(); - node.visitReferrers(highwayVisitor); - return highwayVisitor.onlyHighways; - } - - private static boolean basicNodeChecks(INode nearNode, INode node) { - return node != null && nearNode != null && !node.isDeleted() && !nearNode.isDeleted() && !nearNode.equals(node) - && node.isLatLonKnown() && nearNode.isLatLonKnown(); - } - - private static void mergeWays(DataSet dataSet) { - for (final var way1 : dataSet.getWays()) { - if (way1.isDeleted()) { - continue; - } - final var bbox = new BBox(); - bbox.addPrimitive(way1, DEGREE_BUFFER); - for (var nearbyWay : dataSet.searchWays(bbox)) { - if (nearbyWay.getNodes().stream().filter(way1::containsNode).count() > 1) { - for (var entry : checkWayDuplications(way1, nearbyWay).entrySet()) { - GetDataRunnable.addMissingElement(entry); - } - } - } - } - } - - protected static void addMissingElement(Map.Entry, List>> entry) { - final var way = entry.getKey().getWay(); - final Way waySegmentWay; - try { - waySegmentWay = entry.getKey().toWay(); - } catch (ReflectiveOperationException e) { - throw new JosmRuntimeException(e); - } - final var toAdd = entry.getValue().stream().flatMap(seg -> Stream.of(seg.getFirstNode(), seg.getSecondNode())) - .filter(node -> !waySegmentWay.containsNode(node)).findAny().orElse(null); - if ((toAdd != null) && (convertToMeters( - Geometry.getDistance(waySegmentWay, toAdd)) < (MapWithAIPreferenceHelper.getMaxNodeDistance() * 10))) { - way.addNode(entry.getKey().getUpperIndex(), toAdd); - } - for (var i = 0; i < (way.getNodesCount() - 2); i++) { - final var node0 = way.getNode(i); - final var node3 = way.getNode(i + 2); - if (node0.equals(node3)) { - final var nodes = way.getNodes(); - nodes.remove(i + 2); // NOSONAR SonarLint doesn't like this (if it was i instead of i + 2, it would - // be an issue) - way.setNodes(nodes); - } - } - } - - protected static double convertToMeters(double value) { - return value * ProjectionRegistry.getProjection().getMetersPerUnit(); - } - - protected static void cleanupArtifacts(Way way) { - for (var i = 0; i < (way.getNodesCount() - 2); i++) { - final var node0 = way.getNode(i); - final var node1 = way.getNode(i + 1); - final var node2 = way.getNode(i + 2); - final double angle = Geometry.getCornerAngle(node0.getEastNorth(), node1.getEastNorth(), - node2.getEastNorth()); - if (angle < ARTIFACT_ANGLE) { - final List nodes = way.getNodes(); - nodes.remove(i + 1); // NOSONAR not an issue since I'm adding it back - nodes.add(i + 2, node1); - } - } - if ((way.getNodesCount() == 2) && (way.getDataSet() != null)) { - final var tBBox = new BBox(); - tBBox.addPrimitive(way, DEGREE_BUFFER); - if (way.getDataSet().searchWays(tBBox).stream().filter(not(IPrimitive::isDeleted)).filter(not(way::equals)) - .anyMatch(tWay -> way.getNodes().stream().filter( - tNode -> Geometry.getDistance(tNode, tWay) < MapWithAIPreferenceHelper.getMaxNodeDistance()) - .count() == way.getNodesCount())) { - way.setDeleted(true); - } - } - } - - /** - * Check for nearly duplicate way sections - * - * @param way1 The way to map duplicate segments to - * @param way2 The way that may have duplicate segments - * @return A Map<WaySegment to modify from way1, List<WaySegments from - * way2> to make the segment conform to > - */ - protected static Map, List>> checkWayDuplications(Way way1, - Way way2) { - final var waySegments1 = way1.getNodePairs(false).stream() - .map(pair -> IWaySegment.forNodePair(way1, pair.a, pair.b)).toList(); - final var waySegments2 = way2.getNodePairs(false).stream() - .map(pair -> IWaySegment.forNodePair(way2, pair.a, pair.b)).toList(); - final var partials = new TreeMap, List>>(); - final BiPredicate, IWaySegment> connected = (segment1, - segment2) -> segment1.getFirstNode().equals(segment2.getFirstNode()) - || segment1.getSecondNode().equals(segment2.getFirstNode()) - || segment1.getFirstNode().equals(segment2.getSecondNode()) - || segment1.getSecondNode().equals(segment2.getSecondNode()); - for (final var segment1 : waySegments1) { - final var replacements = waySegments2.stream().filter(seg2 -> connected.test(segment1, seg2)) - .filter(seg -> { - final var node2 = segment1.getFirstNode().equals(seg.getFirstNode()) - || segment1.getSecondNode().equals(seg.getFirstNode()) ? seg.getFirstNode() - : seg.getSecondNode(); - final var node1 = node2.equals(seg.getFirstNode()) ? seg.getSecondNode() : seg.getFirstNode(); - final var node3 = segment1.getFirstNode().equals(node2) ? segment1.getSecondNode() - : segment1.getFirstNode(); - return Math.abs(Geometry.getCornerAngle(node1.getEastNorth(), node2.getEastNorth(), - node3.getEastNorth())) < (Math.PI / 4); - }).collect(Collectors.toList()); - if ((replacements.size() != 2) || replacements.stream() - .anyMatch(seg -> Arrays.asList(segment1.getFirstNode(), segment1.getSecondNode()) - .containsAll(Arrays.asList(seg.getFirstNode(), seg.getSecondNode())))) { - continue; - } - partials.put(segment1, replacements); - } - return partials; - } - - /** - * Actually get the data - * - * @param bounds The bounds to get the data from - * @param monitor Use to determine if the operation has been cancelled - * @return A dataset with the data from the bounds - */ - private static DataSet getDataReal(Bounds bounds, ProgressMonitor monitor) { - final var dataSet = new DataSet(); - dataSet.setUploadPolicy(UploadPolicy.DISCOURAGED); - - final var tasks = new ArrayList>(); - final var pool = MapWithAIDataUtils.getForkJoinPool(); - for (var map : new ArrayList<>(MapWithAILayerInfo.getInstance().getLayers())) { - tasks.add(pool.submit( - MapWithAIDataUtils.download(monitor, bounds, map, MapWithAIDataUtils.MAXIMUM_SIDE_DIMENSIONS))); - } - for (var task : tasks) { - dataSet.mergeFrom(task.join()); - } - dataSet.setUploadPolicy(UploadPolicy.BLOCKED); - return dataSet; - } - - /** - * Add source tags to primitives - * - * @param dataSet The dataset to add the mapwithai source tag to (not visible on - * object post-upload) - * @param source The source to associate with the data - * @return The dataset for easy chaining - */ - public static DataSet addMapWithAISourceTag(DataSet dataSet, String source) { - return addTag(dataSet, MAPWITHAI_SOURCE_TAG_KEY, source); - } - - /** - * Add source tags to primitives - * - * @param dataSet The dataset to add the source tag to (visible on object - * post-upload) - * @param source The source to associate with the data - * @return The dataset for easy chaining - */ - public static DataSet addSourceTag(DataSet dataSet, String source) { - return addTag(dataSet, SOURCE_TAG_KEY, source); - } - - private static DataSet addTag(DataSet dataSet, String key, String value) { - dataSet.getNodes().stream().filter(p -> checkIfMapWithAISourceShouldBeAdded(p, key)) - .filter(node -> node.getReferrers().isEmpty()).forEach(node -> node.put(key, value)); - dataSet.getWays().stream().filter(p -> checkIfMapWithAISourceShouldBeAdded(p, key)) - .forEach(way -> way.put(key, value)); - dataSet.getRelations().stream().filter(p -> checkIfMapWithAISourceShouldBeAdded(p, key)) - .forEach(rel -> rel.put(key, value)); - return dataSet; - } - - private static boolean checkIfMapWithAISourceShouldBeAdded(OsmPrimitive prim, String key) { - return !prim.isDeleted() && !prim.hasTag(key); - } - - /** - * Set the info that is being used to download data - * - * @param info The info being used - */ - public void setMapWithAIInfo(MapWithAIInfo info) { - this.info = info; - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAction.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAction.java deleted file mode 100644 index 8829fd12..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAction.java +++ /dev/null @@ -1,161 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static java.util.function.Predicate.not; -import static org.openstreetmap.josm.gui.help.HelpUtil.ht; -import static org.openstreetmap.josm.tools.I18n.marktr; -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.Action; -import javax.swing.JOptionPane; - -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.io.Serial; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.TreeMap; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.actions.AbstractMergeAction; -import org.openstreetmap.josm.actions.JosmAction; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.Notification; -import org.openstreetmap.josm.gui.layer.Layer; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.tools.Shortcut; - -/** - * Create or download MapWithAI data - */ -public class MapWithAIAction extends JosmAction { - /** UID */ - @Serial - private static final long serialVersionUID = 8886705479253246588L; - private static final String DOWNLOAD_DATA = marktr("{0}: Download Data"); - private static final String SWITCH_LAYERS = marktr("{0}: Switch Layers"); - - /** - * Create the action - */ - public MapWithAIAction() { - super(tr(DOWNLOAD_DATA, MapWithAIPlugin.NAME), "mapwithai", tr("Get data from {0}", MapWithAIPlugin.NAME), - Shortcut.registerShortcut("data:mapWithAI", tr("Data: {0}", MapWithAIPlugin.NAME), KeyEvent.VK_R, - Shortcut.CTRL), - true, "mapwithai:downloadData", true); - setHelpId(ht("Plugin/MapWithAI#BasicUsage")); - } - - @Override - public void actionPerformed(ActionEvent event) { - if (isEnabled()) { - final boolean hasLayer = MapWithAIDataUtils.getLayer(false) != null; - final var osmLayers = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream() - .filter(not(MapWithAILayer.class::isInstance)).filter(Layer::isVisible) - .collect(Collectors.toList()); - final var layer = getOsmLayer(osmLayers); - if ((layer != null) && MapWithAIDataUtils.getMapWithAIData(MapWithAIDataUtils.getLayer(true), layer)) { - final var notification = createMessageDialog(); - if (notification != null) { - notification.show(); - } - } else if ((layer != null) && hasLayer) { - toggleLayer(layer); - } - } - } - - /** - * Get the osm layer that the user wants to use to get data from (doesn't ask if - * user only has one data layer) - * - * @param osmLayers The list of osm data layers - * @return The layer that the user selects - */ - protected static OsmDataLayer getOsmLayer(List osmLayers) { - OsmDataLayer returnLayer = null; - final var tLayers = new ArrayList<>(osmLayers); - if (DetectTaskingManagerUtils.hasTaskingManagerLayer()) { - tLayers.removeIf(DetectTaskingManagerUtils.getTaskingManagerLayer()::equals); - } - if (tLayers.size() == 1) { - returnLayer = osmLayers.get(0); - } else if (!tLayers.isEmpty()) { - returnLayer = AbstractMergeAction.askTargetLayer(osmLayers.toArray(new OsmDataLayer[0]), - tr("Please select the initial layer for boundaries"), tr("Select target layer for boundaries"), - tr("OK"), "download"); - } - return returnLayer; - } - - /** - * Toggle the layer (the toLayer is the layer to switch to, if currently active - * it will switch to the MapWithAI layer, if the MapWithAI layer is currently - * active it will switch to the layer passed) - * - * @param toLayer The {@link Layer} to switch to - */ - protected static void toggleLayer(Layer toLayer) { - final var mapwithai = MapWithAIDataUtils.getLayer(false); - final var currentLayer = MainApplication.getLayerManager().getActiveLayer(); - if (currentLayer != null) { - if (currentLayer.equals(mapwithai) && (toLayer != null)) { - MainApplication.getLayerManager().setActiveLayer(toLayer); - } else if (currentLayer.equals(toLayer) && (mapwithai != null)) { - MainApplication.getLayerManager().setActiveLayer(mapwithai); - } - } - } - - @Override - protected void updateEnabledState() { - setEnabled(getLayerManager().getEditDataSet() != null); - if (this.isEnabled()) { - if (MapWithAIDataUtils.getLayer(false) == null) { - putValue(Action.NAME, tr(DOWNLOAD_DATA, MapWithAIPlugin.NAME)); - } else { - putValue(Action.NAME, tr(SWITCH_LAYERS, MapWithAIPlugin.NAME)); - } - } - } - - /** - * Create a message dialog to notify the user if data is available in their - * downloaded region - * - * @return A Notification to show ({@link Notification#show}) - */ - public static Notification createMessageDialog() { - final var layer = MapWithAIDataUtils.getLayer(false); - final var notification = layer == null ? null : new Notification(); - if (notification != null) { - final var bounds = new ArrayList<>(layer.getDataSet().getDataSourceBounds()); - if (bounds.isEmpty()) { - MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream() - .map(OsmDataLayer::getDataSet).filter(Objects::nonNull).map(DataSet::getDataSourceBounds) - .forEach(bounds::addAll); - } - final var message = new StringBuilder(); - message.append(MapWithAIPlugin.NAME).append(": "); - DataAvailability.getInstance(); // force initialization, if it hasn't already occured - final var availableTypes = new TreeMap(); - for (final var bound : bounds) { - DataAvailability.getDataTypes(bound.getCenter()) - .forEach((type, available) -> availableTypes.merge(type, available, Boolean::logicalOr)); - } - if (availableTypes.isEmpty()) { - message.append(tr("No data available")); - } else { - message.append("Data available: ").append(String.join(", ", availableTypes.keySet())); - } - - notification.setContent(message.toString()); - notification.setDuration(Notification.TIME_DEFAULT); - notification.setIcon(JOptionPane.INFORMATION_MESSAGE); - } - return notification; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java deleted file mode 100644 index c5977de3..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java +++ /dev/null @@ -1,503 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static java.util.function.Predicate.not; -import static org.openstreetmap.josm.gui.help.HelpUtil.ht; -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.JOptionPane; - -import java.net.SocketTimeoutException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.TreeSet; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.ForkJoinTask; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.UndoRedoHandler; -import org.openstreetmap.josm.data.coor.ILatLon; -import org.openstreetmap.josm.data.coor.LatLon; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; -import org.openstreetmap.josm.gui.ExceptionDialogUtil; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.Notification; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.gui.progress.ProgressMonitor; -import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.io.IllegalDataException; -import org.openstreetmap.josm.io.OsmTransferException; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.commands.MapWithAIAddCommand; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.tools.ExceptionUtil; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Utils; - -/** - * Various utility methods - * - * @author Taylor Smock - * - */ -public final class MapWithAIDataUtils { - /** The maximum dimensions for MapWithAI data (in kilometers) */ - public static final int MAXIMUM_SIDE_DIMENSIONS = 10_000; // RapiD is about 1 km, max is 10 km, but 10 km causes - // timeouts - private static final int TOO_MANY_BBOXES = 4; - /** - * {@code true} if we need a fork join pool that is not the - * {@link ForkJoinPool#commonPool()} - */ - private static Boolean requiresForkJoinPool; - private static ForkJoinPool forkJoinPool; - static final Object LAYER_LOCK = new Object(); - - private MapWithAIDataUtils() { - // Hide the constructor - } - - /** - * Add primitives and their children to a collection - * - * @param collection A collection to add the primitives to - * @param primitives The primitives to add to the collection - */ - public static void addPrimitivesToCollection(Collection collection, - Collection primitives) { - final var temporaryCollection = new TreeSet(); - for (final var primitive : primitives) { - if (primitive instanceof Way way) { - temporaryCollection.addAll(way.getNodes()); - } else if (primitive instanceof Relation relation) { - addPrimitivesToCollection(temporaryCollection, relation.getMemberPrimitives()); - } - temporaryCollection.add(primitive); - } - collection.addAll(temporaryCollection); - } - - /** - * Add specified source tags to objects without a source tag that also have a - * specific key - * - * @param dataSet The {#link DataSet} to look through - * @param primaryKey The primary key that must be in the {@link OsmPrimitive} - * @param source The specified source value (not tag) - */ - public static void addSourceTags(DataSet dataSet, String primaryKey, String source) { - dataSet.allPrimitives().stream().filter(p -> p.hasKey(primaryKey) && !p.hasKey("source")).forEach(p -> { - p.put("source", source); - p.save(); - }); - } - - /** - * Get a dataset from the API servers using a list bounds - * - * @param bounds The bounds from which to get data - * @param maximumDimensions The maximum dimensions to try to download at any one - * time - * @return A DataSet with data inside the bounds - */ - public static DataSet getData(Collection bounds, int maximumDimensions) { - final var dataSet = new DataSet(); - final var realBounds = bounds.stream().filter(b -> !b.isOutOfTheWorld()).distinct() - .flatMap(bound -> MapWithAIDataUtils.reduceBoundSize(bound, maximumDimensions).stream()) - .collect(Collectors.toList()); - if (!MapWithAIPreferenceHelper.getMapWithAIUrl().isEmpty()) { - if ((bounds.size() < TOO_MANY_BBOXES) || confirmBigDownload(realBounds)) { - final var monitor = new PleaseWaitProgressMonitor(); - monitor.beginTask(tr("Downloading {0} Data", MapWithAIPlugin.NAME), realBounds.size()); - try { - final var urls = new ArrayList<>(MapWithAIPreferenceHelper.getMapWithAIUrl()); - final var downloadedDataSets = new ArrayList>( - realBounds.size() * urls.size()); - for (var bound : realBounds) { - for (var url : urls) { - if (url.getUrl() != null && !Utils.isStripEmpty(url.getUrl())) { - final var ds = download(monitor, bound, url, maximumDimensions); - downloadedDataSets.add(ds); - MapWithAIDataUtils.getForkJoinPool().execute(ds); - } - } - } - mergeDataSets(dataSet, downloadedDataSets); - } finally { - monitor.finishTask(); - monitor.close(); - } - } - } else { - final var noUrls = GuiHelper.runInEDTAndWaitAndReturn( - () -> MapWithAIPreferenceHelper.getMapWithAIUrl().isEmpty() ? new Notification(tr( - "There are no defined URLs. Attempting to add the appropriate servers.\nPlease try again.")) - : new Notification(tr("No URLS are enabled"))); - Objects.requireNonNull(noUrls); - noUrls.setDuration(Notification.TIME_DEFAULT); - noUrls.setIcon(JOptionPane.INFORMATION_MESSAGE); - noUrls.setHelpTopic(ht("Plugin/MapWithAI#Preferences")); - GuiHelper.runInEDT(noUrls::show); - if (MapWithAIPreferenceHelper.getMapWithAIUrl().isEmpty() - && MapWithAILayerInfo.getInstance().getDefaultLayers().isEmpty()) { - MapWithAILayerInfo.getInstance().loadDefaults(true, MapWithAIDataUtils.getForkJoinPool(), false, - () -> Logging.info("MapWithAI Sources: Initialized sources")); - } - } - return dataSet; - } - - /** - * Download an area - * - * @param monitor The monitor to update - * @param bound The bounds that are being downloading - * @param mapWithAIInfo The source of the data - * @param maximumDimensions The maximum dimensions to download - * @return A future that will have downloaded the data - */ - public static ForkJoinTask download(ProgressMonitor monitor, Bounds bound, MapWithAIInfo mapWithAIInfo, - int maximumDimensions) { - return ForkJoinTask.adapt(() -> { - final var downloader = new BoundingBoxMapWithAIDownloader(bound, mapWithAIInfo, - DetectTaskingManagerUtils.hasTaskingManagerLayer()); - try { - return downloader.parseOsm(monitor.createSubTaskMonitor(1, false)); - } catch (OsmTransferException e) { - if (e.getCause() instanceof SocketTimeoutException && maximumDimensions > MAXIMUM_SIDE_DIMENSIONS / 10 - && maximumDimensions / 2f > 0.5) { - return getData(Collections.singleton(bound), maximumDimensions / 2); - } - throw e; - } - }); - } - - /** - * Merge datasets - * - * @param original The original dataset - * @param dataSetsToMerge The datasets to merge (futures) - */ - private static void mergeDataSets(final DataSet original, final List> dataSetsToMerge) { - for (var ds : dataSetsToMerge) { - try { - original.mergeFrom(ds.join()); - } catch (RuntimeException e) { - final String notificationMessage; - Throwable cause = e.getCause(); - if (cause != null) { - while (cause.getCause() != null && RuntimeException.class.equals(cause.getClass())) { - cause = cause.getCause(); - } - } - if (cause instanceof IllegalDataException) { - notificationMessage = ExceptionUtil.explainException((Exception) cause); - Logging.trace(e); - final var notification = new Notification(); - GuiHelper.runInEDT(() -> notification.setContent(notificationMessage)); - GuiHelper.runInEDT(notification::show); - } else if (cause instanceof OsmTransferException osmTransferException) { - GuiHelper.runInEDT(() -> ExceptionDialogUtil.explainException(osmTransferException)); - } else { - throw e; - } - } - } - } - - /** - * Confirm a large download - * - * @param realBounds The list of bounds that will be downloaded - * @return {@code true} if the user still wants to download data - */ - private static synchronized boolean confirmBigDownload(List realBounds) { - final var confirmation = new AtomicBoolean(false); - // This is not a separate class since we don't want to show multiple - // confirmation dialogs - // which is why this method is synchronized. - GuiHelper.runInEDTAndWait(() -> { - final var confirmed = ConditionalOptionPaneUtil.showConfirmationDialog( - MapWithAIPlugin.NAME.concat(".alwaysdownload"), null, - tr("You are going to make {0} requests to the MapWithAI server. This may take some time.
Continue?", - realBounds.size()), - null, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, JOptionPane.YES_OPTION); - confirmation.set(confirmed); - }); - return confirmation.get(); - } - - /** - * Get a ForkJoinPool that is safe for use in Webstart - * - * @return The {@link ForkJoinPool} for MapWithAI use. - */ - public static ForkJoinPool getForkJoinPool() { - if (requiresForkJoinPool == null) { - requiresForkJoinPool = Utils.isRunningWebStart() || System.getSecurityManager() != null; - } - if (requiresForkJoinPool) { - synchronized (MapWithAIDataUtils.class) { - if (Objects.isNull(forkJoinPool) || forkJoinPool.isShutdown()) { - forkJoinPool = Utils.newForkJoinPool(MapWithAIPlugin.NAME.concat(".forkjoinpoolthreads"), - MapWithAIPlugin.NAME, Thread.NORM_PRIORITY); - } - } - return forkJoinPool; - } - return ForkJoinPool.commonPool(); - } - - /** - * Get the height of a bounds - * - * @param bounds The bounds with lat/lon information - * @return The height in meters (see {@link LatLon#greatCircleDistance}) - */ - public static double getHeight(Bounds bounds) { - final var topRight = bounds.getMax(); - final var bottomLeft = bounds.getMin(); - final double minx = bottomLeft.getX(); - final double maxY = topRight.getY(); - final var topLeft = new LatLon(maxY, minx); - return bottomLeft.greatCircleDistance((ILatLon) topLeft); - } - - /** - * Get the first {@link MapWithAILayer} that we can find. - * - * @param create true if we want to create a new layer - * @return A MapWithAILayer, or a new MapWithAILayer if none exist. May return - * {@code null} if {@code create} is {@code false}. - */ - public static MapWithAILayer getLayer(boolean create) { - final var mapWithAILayers = MainApplication.getLayerManager().getLayersOfType(MapWithAILayer.class); - MapWithAILayer layer = null; - synchronized (LAYER_LOCK) { - if (mapWithAILayers.isEmpty() && create) { - layer = new MapWithAILayer(new DataSet(), MapWithAIPlugin.NAME, null); - } else if (!mapWithAILayers.isEmpty()) { - layer = mapWithAILayers.get(0); - } - } - - final var tLayer = layer; - if (!MainApplication.getLayerManager().getLayers().contains(tLayer) && create) { - GuiHelper.runInEDTAndWait(() -> MainApplication.getLayerManager().addLayer(tLayer)); - } - - return layer; - } - - /** - * Get data for a {@link MapWithAILayer} - * - * @param layer The {@link MapWithAILayer} to add data to - * @return true if data was downloaded - */ - public static boolean getMapWithAIData(MapWithAILayer layer) { - final var osmLayers = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream() - .filter(not(MapWithAILayer.class::isInstance)).toList(); - var gotData = false; - for (final var osmLayer : osmLayers) { - if (!osmLayer.isLocked() && getMapWithAIData(layer, osmLayer)) { - gotData = true; - } - } - return gotData; - } - - /** - * Get the data for MapWithAI - * - * @param layer A pre-existing {@link MapWithAILayer} - * @param osmLayer The osm datalayer with a set of bounds - * @return true if data was downloaded - */ - public static boolean getMapWithAIData(MapWithAILayer layer, OsmDataLayer osmLayer) { - return getMapWithAIData(layer, osmLayer.getDataSet().getDataSourceBounds()); - } - - /** - * Get the data for MapWithAI - * - * @param layer A pre-existing {@link MapWithAILayer} - * @param bounds The bounds to get the data in - * @return true if data was downloaded - */ - public static boolean getMapWithAIData(MapWithAILayer layer, Bounds... bounds) { - return getMapWithAIData(layer, Arrays.asList(bounds)); - } - - /** - * Get the data for MapWithAI - * - * @param layer A pre-existing {@link MapWithAILayer} - * @param bounds The bounds to get the data in - * @return true if data was downloaded - */ - public static boolean getMapWithAIData(MapWithAILayer layer, Collection bounds) { - final var mapWithAISet = layer.getDataSet(); - final var area = mapWithAISet.getDataSourceArea(); - final var toDownload = area == null ? new ArrayList<>(bounds) - : bounds.stream().filter(Objects::nonNull).filter(tBounds -> !area.contains(tBounds.asRect())).toList(); - if (!toDownload.isEmpty()) { - getForkJoinPool().execute(() -> { - final var newData = getData(toDownload, MAXIMUM_SIDE_DIMENSIONS); - final var lock = layer.getLock(); - lock.lock(); - try { - mapWithAISet.update(() -> mapWithAISet.mergeFrom(newData)); - GetDataRunnable.cleanup(mapWithAISet, null, null); - } finally { - lock.unlock(); - } - toDownload.forEach(layer::onPostDownloadFromServer); - }); - } - return !toDownload.isEmpty(); - } - - /** - * Get the width of a bounds - * - * @param bounds The bounds to get the width of - * @return See {@link LatLon#greatCircleDistance} - */ - public static double getWidth(Bounds bounds) { - // Lat is y, Lon is x - final var bottomLeft = bounds.getMin(); - final var topRight = bounds.getMax(); - final double minX = bottomLeft.getX(); - final double maxX = topRight.getX(); - final double minY = bottomLeft.getY(); - final double maxY = topRight.getY(); - final var bottomRight = new LatLon(minY, maxX); - final var topLeft = new LatLon(maxY, minX); - return Math.max(bottomLeft.greatCircleDistance((ILatLon) bottomRight), - topLeft.greatCircleDistance((ILatLon) topRight)); - } - - /** - * Reduce a bound to the specified dimensions, returning a list of bounds. - * - * @param bound The bound to reduce to a set maximum dimension - * @param maximumDimensions The maximum side dimensions of the bound - * @return A list of Bounds that have a dimension no more than - * {@code maximumDimensions} - */ - public static List reduceBoundSize(Bounds bound, int maximumDimensions) { - final var returnBounds = new ArrayList(); - final double width = getWidth(bound); - final double height = getHeight(bound); - final double widthDivisions = width / maximumDimensions; - final double heightDivisions = height / maximumDimensions; - final int widthSplits = (int) widthDivisions + ((widthDivisions - Math.floor(widthDivisions)) > 0 ? 1 : 0); - final int heightSplits = (int) heightDivisions + ((heightDivisions - Math.floor(heightDivisions)) > 0 ? 1 : 0); - - final double newMinWidths = Math.abs(bound.getMaxLon() - bound.getMinLon()) / widthSplits; - final double newMinHeights = Math.abs(bound.getMaxLat() - bound.getMinLat()) / heightSplits; - - final double minx = bound.getMinLon(); - final double miny = bound.getMinLat(); - for (var x = 1; x <= widthSplits; x++) { - for (var y = 1; y <= heightSplits; y++) { - final var lowerLeft = new LatLon(miny + (newMinHeights * (y - 1)), minx + (newMinWidths * (x - 1))); - final var upperRight = new LatLon(miny + (newMinHeights * y), minx + (newMinWidths * x)); - returnBounds.add(new Bounds(lowerLeft, upperRight)); - } - } - return returnBounds.stream().distinct().collect(Collectors.toList()); - } - - /** - * Reduce a list of bounds to {@link MapWithAIDataUtils#MAXIMUM_SIDE_DIMENSIONS} - * - * @param bounds The bounds to reduce to a set maximum dimension - * @return A list of Bounds that have a dimension no more than - * {@link MapWithAIDataUtils#MAXIMUM_SIDE_DIMENSIONS} - */ - public static List reduceBoundSize(List bounds) { - return reduceBoundSize(bounds, MAXIMUM_SIDE_DIMENSIONS); - } - - /** - * Reduce a list of bounds to a specified size - * - * @param bounds The bounds to reduce to a set maximum dimension - * @param maximumDimensions The maximum width/height dimensions - * @return A list of Bounds that have a dimension no more than the - * {@code maximumDimensions} - */ - public static List reduceBoundSize(List bounds, int maximumDimensions) { - final var returnBounds = new ArrayList(bounds.size()); - bounds.forEach(bound -> returnBounds.addAll(reduceBoundSize(bound, maximumDimensions))); - return returnBounds.stream().distinct().toList(); - } - - /** - * Remove primitives and their children from a dataset. - * - * @param primitives The primitives to remove - */ - public static void removePrimitivesFromDataSet(Collection primitives) { - for (final var primitive : primitives) { - if (primitive instanceof Relation relation) { - removePrimitivesFromDataSet(relation.getMemberPrimitives()); - } else if (primitive instanceof Way way) { - for (final var node : way.getNodes()) { - final var ds = node.getDataSet(); - if (ds != null) { - ds.removePrimitive(node); - } - } - } - final var ds = primitive.getDataSet(); - if (ds != null) { - ds.removePrimitive(primitive); - } - } - } - - /** - * Get the number of whole objects added from the MapWithAI layer. A whole - * object is an object with tags or not a member of another object. - * - * @return The number of objects added from the MapWithAI data layer - */ - public static Long getAddedObjects() { - return Optional - .ofNullable(GuiHelper.runInEDTAndWaitAndReturn(() -> UndoRedoHandler.getInstance().getUndoCommands())) - .map(commands -> commands.stream().filter(MapWithAIAddCommand.class::isInstance) - .map(MapWithAIAddCommand.class::cast).mapToLong(MapWithAIAddCommand::getAddedObjects).sum()) - .orElse(0L); - } - - /** - * Get source tags for objects added from the MapWithAI data layer - * - * @return The source tags for Objects added from the MapWithAI data layer - */ - public static List getAddedObjectsSource() { - return Optional - .ofNullable(GuiHelper.runInEDTAndWaitAndReturn(() -> UndoRedoHandler.getInstance().getUndoCommands())) - .map(commands -> commands.stream().filter(MapWithAIAddCommand.class::isInstance) - .map(MapWithAIAddCommand.class::cast).flatMap(com -> com.getSourceTags().stream()).distinct() - .collect(Collectors.toList())) - .orElseGet(Collections::emptyList); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAILayer.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAILayer.java deleted file mode 100644 index f6c83a78..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAILayer.java +++ /dev/null @@ -1,446 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.Icon; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.SwingConstants; - -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.io.File; -import java.io.IOException; -import java.io.Serial; -import java.io.Serializable; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.TreeSet; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.actions.ExpertToggleAction; -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.DataSource; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.DownloadPolicy; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.UploadPolicy; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.Notification; -import org.openstreetmap.josm.gui.dialogs.layer.DuplicateAction; -import org.openstreetmap.josm.gui.layer.Layer; -import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent; -import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; -import org.openstreetmap.josm.gui.mappaint.StyleSource; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.gui.widgets.HtmlPanel; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.tools.BlacklistUtils; -import org.openstreetmap.josm.plugins.mapwithai.tools.MapPaintUtils; -import org.openstreetmap.josm.spi.preferences.Config; -import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent; -import org.openstreetmap.josm.spi.preferences.PreferenceChangedListener; -import org.openstreetmap.josm.tools.GBC; -import org.openstreetmap.josm.tools.ImageProvider; -import org.openstreetmap.josm.tools.Utils; - -/** - * This layer shows MapWithAI data. For various reasons, we currently only allow - * one to be created, although this may change. - * - * @author Taylor Smock - * - */ -public class MapWithAILayer extends OsmDataLayer implements ActiveLayerChangeListener, PreferenceChangedListener { - private static final Collection COMPACT = Collections.singleton("esri"); - private Integer maximumAddition; - private MapWithAIInfo url; - private Boolean switchLayers; - private boolean continuousDownload = true; - private final Lock lock; - private final HashSet downloadedInfo = new HashSet<>(); - - /** - * Create a new MapWithAI layer - * - * @param data OSM data from MapWithAI - * @param name Layer name - * @param associatedFile an associated file (can be null) - */ - public MapWithAILayer(DataSet data, String name, File associatedFile) { - super(data, name, associatedFile); - data.setUploadPolicy(UploadPolicy.BLOCKED); - data.setDownloadPolicy(DownloadPolicy.BLOCKED); - lock = new MapLock(); - MainApplication.getLayerManager().addActiveLayerChangeListener(this); - new ContinuousDownloadAction(this); // Initialize data source listeners - Config.getPref().addKeyPreferenceChangeListener("download.mapwithai.data", this); - } - - @Override - public String getChangesetSourceTag() { - if (MapWithAIDataUtils.getAddedObjects() > 0) { - TreeSet sources = MapWithAIDataUtils.getAddedObjectsSource().stream().filter(Objects::nonNull) - .map(string -> COMPACT.stream().filter(string::contains).findAny().orElse(string)) - .collect(Collectors.toCollection(TreeSet::new)); - sources.add("MapWithAI"); - return String.join("; ", sources); - } - return null; - } - - public void setMaximumAddition(Integer max) { - maximumAddition = max; - } - - public Integer getMaximumAddition() { - return maximumAddition; - } - - public void setMapWithAIUrl(MapWithAIInfo info) { - this.url = info; - } - - public MapWithAIInfo getMapWithAIUrl() { - return url; - } - - public void setSwitchLayers(boolean selected) { - switchLayers = selected; - } - - public Boolean isSwitchLayers() { - return switchLayers; - } - - @Override - public Object getInfoComponent() { - final Object p = super.getInfoComponent(); - if (p instanceof JPanel panel) { - if (maximumAddition != null) { - panel.add(new JLabel(tr("Maximum Additions: {0}", maximumAddition), SwingConstants.CENTER), - GBC.eop().insets(15, 0, 0, 0)); - } - if (url != null) { - panel.add(new JLabel(tr("URL: {0}", url.getUrlExpanded()), SwingConstants.CENTER), - GBC.eop().insets(15, 0, 0, 0)); - } - if (switchLayers != null) { - panel.add(new JLabel(tr("Switch Layers: {0}", switchLayers), SwingConstants.CENTER), - GBC.eop().insets(15, 0, 0, 0)); - } - } - return p; - } - - @Override - public Action[] getMenuEntries() { - Collection> forbiddenActions = Arrays.asList(LayerSaveAction.class, - LayerSaveAsAction.class, DuplicateAction.class, LayerGpxExportAction.class, - ConvertToGpxLayerAction.class); - final List actions = Arrays.stream(super.getMenuEntries()) - .filter(action -> forbiddenActions.stream().noneMatch(clazz -> clazz.isInstance(action))) - .collect(Collectors.toCollection(ArrayList::new)); - if (actions.isEmpty()) { - actions.add(new ContinuousDownloadAction(this)); - } else { - actions.add(actions.size() - 2, new ContinuousDownloadAction(this)); - } - return actions.toArray(new Action[0]); - } - - public Lock getLock() { - return lock; - } - - @Override - public void preferenceChanged(PreferenceChangeEvent e) { - if ("download.mapwithai.data".equals(e.getKey())) { - final Object value = e.getNewValue().getValue(); - if (value instanceof Boolean bool) { - this.continuousDownload = bool; - } else if (value instanceof String str) { - this.continuousDownload = Boolean.parseBoolean(str); - } else { - this.continuousDownload = false; - } - } - } - - private class MapLock extends ReentrantLock { - @Serial - private static final long serialVersionUID = 5441350396443132682L; - private boolean dataSetLocked; - - public MapLock() { - super(); - // Do nothing - } - - @Override - public void lock() { - super.lock(); - dataSetLocked = getDataSet().isLocked(); - if (dataSetLocked) { - getDataSet().unlock(); - } - } - - @Override - public void unlock() { - super.unlock(); - if (dataSetLocked) { - getDataSet().lock(); - } - } - } - - @Override - public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) { - if (checkIfToggleLayer()) { - final StyleSource style = MapPaintUtils.getMapWithAIPaintStyle(); - if (style.active != this.equals(MainApplication.getLayerManager().getActiveLayer())) { - MapPaintStyles.toggleStyleActive(MapPaintStyles.getStyles().getStyleSources().indexOf(style)); - } - } - } - - private static boolean checkIfToggleLayer() { - final List keys = Config.getPref().getKeySet().parallelStream() - .filter(string -> string.contains(MapWithAIPlugin.NAME) && string.contains("boolean:toggle_with_layer")) - .toList(); - boolean toggle = false; - if (keys.size() == 1) { - toggle = Config.getPref().getBoolean(keys.get(0), false); - } - return toggle; - } - - @Override - public synchronized void destroy() { - Config.getPref().removeKeyPreferenceChangeListener("download.mapwithai.data", this); - super.destroy(); - MainApplication.getLayerManager().removeActiveLayerChangeListener(this); - } - - @Override - public Icon getIcon() { - return ImageProvider.getIfAvailable("mapwithai") == null ? super.getIcon() - : ImageProvider.get("mapwithai", ImageProvider.ImageSizes.LAYER); - } - - /** - * Call after download from server - * - * @param bounds The newly added bounds - */ - public void onPostDownloadFromServer(Bounds bounds) { - super.onPostDownloadFromServer(); - GetDataRunnable.cleanup(getDataSet(), bounds, null); - if (!this.data.getDataSourceBounds().contains(bounds)) { - this.data.addDataSource(new DataSource(bounds, null)); - } - } - - @Override - public void selectionChanged(SelectionChangeEvent event) { - if (BlacklistUtils.isBlacklisted()) { - if (!event.getSelection().isEmpty()) { - GuiHelper.runInEDT(() -> getDataSet().setSelected(Collections.emptySet())); - createBadDataNotification(); - } - return; - } - super.selectionChanged(event); - final int maximumAdditionSelection = MapWithAIPreferenceHelper.getMaximumAddition(); - if ((event.getSelection().size() - event.getOldSelection().size() > 1 - || maximumAdditionSelection < event.getSelection().size()) - && (MapWithAIPreferenceHelper.getMaximumAddition() != 0 || !ExpertToggleAction.isExpert())) { - Collection selection; - final Collection oldWays = Utils.filteredCollection(event.getOldSelection(), Way.class); - if (Utils.filteredCollection(event.getSelection(), Node.class).stream() - .filter(n -> !event.getOldSelection().contains(n)) - .allMatch(n -> oldWays.stream().anyMatch(w -> w.containsNode(n)))) { - selection = event.getSelection(); - } else { - OsmComparator comparator = new OsmComparator(event.getOldSelection()); - selection = event.getSelection().stream().distinct().sorted(comparator).limit(maximumAdditionSelection) - .limit(event.getOldSelection().size() + Math.max(1L, maximumAdditionSelection / 10L)) - .collect(Collectors.toList()); - } - GuiHelper.runInEDT(() -> getDataSet().setSelected(selection)); - } - } - - /** - * Create a notification for plugin versions that create bad data. - */ - public static void createBadDataNotification() { - Notification badData = new Notification(); - badData.setIcon(JOptionPane.ERROR_MESSAGE); - badData.setDuration(Notification.TIME_LONG); - HtmlPanel panel = new HtmlPanel(); - StringBuilder message = new StringBuilder() - .append(tr("This version of the MapWithAI plugin is known to create bad data.")).append("
") - .append(tr("Please update plugins and/or JOSM.")); - if (BlacklistUtils.isOffline()) { - message.append("
").append(tr("This message may occur when JOSM is offline.")); - } - panel.setText(message.toString()); - badData.setContent(panel); - MapWithAILayer layer = MapWithAIDataUtils.getLayer(false); - if (layer != null) { - layer.setMaximumAddition(0); - } - GuiHelper.runInEDT(badData::show); - } - - /** - * Compare OsmPrimitives in a custom manner - */ - private record OsmComparator( - Collection previousSelection) implements Comparator, Serializable { - - @Override - public int compare(OsmPrimitive o1, OsmPrimitive o2) { - if (previousSelection.contains(o1) == previousSelection.contains(o2)) { - if (o1.isTagged() == o2.isTagged()) { - return o1.compareTo(o2); - } else if (o1.isTagged()) { - return -1; - } - return 1; - } - if (previousSelection.contains(o1)) { - return -1; - } - return 1; - } - - } - - /** - * Check if we want to download data continuously - * - * @return {@code true} indicates that we should attempt to keep it in sync with - * the data layer(s) - */ - public boolean downloadContinuous() { - return continuousDownload; - } - - /** - * Allow continuous download of data (for the layer that MapWithAI is clamped - * to). - * - * @author Taylor Smock - */ - public static class ContinuousDownloadAction extends AbstractAction implements LayerAction { - @Serial - private static final long serialVersionUID = -3528632887550700527L; - private final transient MapWithAILayer layer; - - /** - * Create a new continuous download toggle - * - * @param layer the layer to toggle continuous download for - */ - public ContinuousDownloadAction(MapWithAILayer layer) { - super(tr("Continuous download")); - new ImageProvider("download").getResource().attachImageIcon(this, true); - this.layer = layer; - updateListeners(); - } - - @Override - public void actionPerformed(ActionEvent e) { - layer.continuousDownload = !layer.continuousDownload; - updateListeners(); - } - - void updateListeners() { - if (layer.continuousDownload) { - for (OsmDataLayer data : MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class)) { - if (!(data instanceof MapWithAILayer)) { - new DownloadListener(data.getDataSet()); - } - } - } else { - DownloadListener.destroyAll(); - } - } - - @Override - public boolean supportLayers(List layers) { - return layers.stream().allMatch(MapWithAILayer.class::isInstance); - } - - @Override - public Component createMenuComponent() { - JCheckBoxMenuItem item = new JCheckBoxMenuItem(this); - item.setSelected(layer.continuousDownload); - return item; - } - - } - - /** - * Check if the layer has downloaded a specific data type - * - * @param info The info to check - * @return {@code true} if the info has been added to the layer - */ - public boolean hasDownloaded(MapWithAIInfo info) { - return downloadedInfo.contains(info); - } - - /** - * Indicate an info has been downloaded in this layer - * - * @param info The info that has been downloaded - */ - public void addDownloadedInfo(MapWithAIInfo info) { - downloadedInfo.add(info); - } - - /** - * Get the info that has been downloaded into this layer - * - * @return An unmodifiable collection of the downloaded info - */ - public Collection getDownloadedInfo() { - return Collections.unmodifiableCollection(downloadedInfo); - } - - @Override - public boolean autosave(File file) throws IOException { - // Consider a deletion a "successful" save. - return Files.deleteIfExists(file.toPath()); - } - - @Override - public boolean isMergable(final Layer other) { - // Don't allow this layer to be merged down - return other instanceof MapWithAILayer; - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIMoveAction.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIMoveAction.java deleted file mode 100644 index aad4c95c..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIMoveAction.java +++ /dev/null @@ -1,152 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static org.openstreetmap.josm.gui.help.HelpUtil.ht; -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.JOptionPane; - -import java.awt.event.ActionEvent; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.actions.ExpertToggleAction; -import org.openstreetmap.josm.actions.JosmAction; -import org.openstreetmap.josm.data.UndoRedoHandler; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.Notification; -import org.openstreetmap.josm.gui.layer.Layer; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.commands.MapWithAIAddCommand; -import org.openstreetmap.josm.plugins.mapwithai.tools.BlacklistUtils; -import org.openstreetmap.josm.tools.Shortcut; - -/** - * Move data between the MapWithAI layer and an OSM data layer - */ -public class MapWithAIMoveAction extends JosmAction { - /** UID for abstract action */ - private static final long serialVersionUID = 319374598; - - /** The maximum number of objects is this times the maximum add */ - public static final long MAX_ADD_MULTIPLIER = 10; - - /** - * Create a new action - */ - public MapWithAIMoveAction() { - super(tr("{0}: Add selected data", MapWithAIPlugin.NAME), "mapwithai", - tr("Add data from {0}", MapWithAIPlugin.NAME), obtainShortcut(), true, "mapwithai:movedata", true); - setHelpId(ht("Plugin/MapWithAI#BasicUsage")); - } - - /** - * @return The default shortcut, if available, or an alternate shortcut that - * makes sense otherwise - */ - private static Shortcut obtainShortcut() { - int key = KeyEvent.VK_A; - int modifier = Shortcut.SHIFT; - final String shortText = "data:mapwithaiadd"; - final Optional shortCut = Shortcut.findShortcut(key, InputEvent.SHIFT_DOWN_MASK); // Shortcut.SHIFT - // maps to - // KeyEvent.SHIFT_DOWN_MASK - if (shortCut.isPresent() && !shortText.equals(shortCut.get().getShortText())) { - key = KeyEvent.VK_C; - modifier = Shortcut.ALT; - } - return Shortcut.registerShortcut(shortText, tr("{0}: {1}", MapWithAIPlugin.NAME, tr("Add selected data")), key, - modifier); - } - - @Override - public void actionPerformed(ActionEvent event) { - if (BlacklistUtils.isBlacklisted()) { - MapWithAILayer.createBadDataNotification(); - return; - } - for (final MapWithAILayer mapWithAI : MainApplication.getLayerManager().getLayersOfType(MapWithAILayer.class)) { - final DataSet ds = mapWithAI.getDataSet(); - final int maxAddition = MapWithAIPreferenceHelper.getMaximumAddition(); - final List nodes = ds.getSelectedNodes().stream().filter(node -> !node.getReferrers().isEmpty()) - .collect(Collectors.toList()); - ds.clearSelection(nodes); - nodes.stream().map(Node::getReferrers).forEach(ds::addSelected); - final Collection selected = limitCollection(ds, maxAddition); - final OsmDataLayer editLayer = getOsmDataLayer(); - if (editLayer != null && !selected.isEmpty() - && (MapWithAIDataUtils.getAddedObjects() < maxAddition * MAX_ADD_MULTIPLIER - || (maxAddition == 0 && ExpertToggleAction.isExpert()))) { - final MapWithAIAddCommand command = new MapWithAIAddCommand(mapWithAI, editLayer, selected); - GuiHelper.runInEDTAndWait(() -> UndoRedoHandler.getInstance().add(command)); - if (MapWithAIPreferenceHelper.isSwitchLayers()) { - MainApplication.getLayerManager().setActiveLayer(editLayer); - } - } else if (MapWithAIDataUtils.getAddedObjects() >= maxAddition * MAX_ADD_MULTIPLIER) { - createTooManyAdditionsNotification(maxAddition); - } - } - } - - private static void createTooManyAdditionsNotification(int maxAddition) { - Notification tooMany = new Notification(); - tooMany.setIcon(JOptionPane.WARNING_MESSAGE); - tooMany.setDuration(Notification.TIME_DEFAULT); - tooMany.setContent( - tr("There is a soft cap of {0} objects before uploading. Please verify everything before uploading.", - maxAddition * MAX_ADD_MULTIPLIER)); - tooMany.show(); - } - - private static Collection limitCollection(DataSet ds, int maxSize) { - return (maxSize > 0 || !ExpertToggleAction.isExpert()) - ? ds.getSelected().stream().limit(maxSize).collect(Collectors.toList()) - : ds.getSelected(); - } - - /** - * Get the OSM Data Layer to add MapWithAI data to - * - * @return An OSM data layer that data can be added to - */ - public static OsmDataLayer getOsmDataLayer() { - return MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream() - .filter(OsmDataLayer::isVisible).filter(OsmDataLayer::isUploadable) - .filter(osmLayer -> !osmLayer.isLocked() && osmLayer.getClass().equals(OsmDataLayer.class)).findFirst() - .orElse(null); - } - - @Override - protected void updateEnabledState() { - setEnabled(checkIfActionEnabled()); - } - - @Override - protected void updateEnabledState(Collection selection) { - if ((selection == null) || selection.isEmpty()) { - setEnabled(false); - } else { - setEnabled(checkIfActionEnabled()); - } - } - - private boolean checkIfActionEnabled() { - boolean returnValue = false; - final Layer active = getLayerManager().getActiveLayer(); - if (active instanceof MapWithAILayer) { - final MapWithAILayer mapWithAILayer = (MapWithAILayer) active; - final Collection selection = mapWithAILayer.getDataSet().getAllSelected(); - returnValue = (selection != null) && !selection.isEmpty(); - } - return returnValue; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIObject.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIObject.java deleted file mode 100644 index 2f47b886..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIObject.java +++ /dev/null @@ -1,94 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.awt.Color; -import java.util.ArrayList; -import java.util.List; - -import org.openstreetmap.josm.data.UndoRedoHandler; -import org.openstreetmap.josm.data.UndoRedoHandler.CommandQueueListener; -import org.openstreetmap.josm.gui.MapStatus; -import org.openstreetmap.josm.gui.widgets.JosmTextField; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.tools.Destroyable; -import org.openstreetmap.josm.tools.GBC; - -/** - * Show the number of MapWithAI objects that have been added since the last time - * the command stack was cleared. The stack is usually cleared on upload. - * - * @author Taylor Smock - * - */ -public class MapWithAIObject implements CommandQueueListener, Destroyable { - private final JosmTextField mapWithAIObjects; - private final List statusLines; - - /** - * Create a new status line for added objects - */ - public MapWithAIObject() { - mapWithAIObjects = new JosmTextField(null, null, "MapWithAI Objects Added: 1000".length() - 10, false); - mapWithAIObjects.setBackground(MapStatus.PROP_BACKGROUND_COLOR.get()); - mapWithAIObjects.setEditable(false); - statusLines = new ArrayList<>(); - UndoRedoHandler.getInstance().addCommandQueueListener(this); - setText(); - } - - /** - * Adds a new status line to the map status - * - * @param mapStatus The status bar to add a count to - */ - public void addMapStatus(MapStatus mapStatus) { - statusLines.add(mapStatus); - mapStatus.add(mapWithAIObjects, GBC.std().insets(3, 0, 0, 0), mapStatus.getComponentCount() - 2); - } - - /** - * Removes a status line from the map status - * - * @param mapStatus The status bar to remove a count from - */ - public void removeMapStatus(MapStatus mapStatus) { - mapStatus.remove(mapWithAIObjects); - statusLines.remove(mapStatus); - } - - @Override - public void commandChanged(int queueSize, int redoSize) { - setText(); - } - - private void setText() { - final long addedObjects = MapWithAIDataUtils.getAddedObjects(); - if (addedObjects == 0L) { - mapWithAIObjects.setVisible(false); - mapWithAIObjects.setVisible(true); - mapWithAIObjects.setText(tr("{0} Objects Added: {1}", MapWithAIPlugin.NAME, addedObjects)); - } else { - mapWithAIObjects.setVisible(true); - mapWithAIObjects.setText(tr("{0} Objects Added: {1}", MapWithAIPlugin.NAME, addedObjects)); - } - final int maxAdd = MapWithAIPreferenceHelper.getMaximumAddition(); - if (addedObjects == 0) { - mapWithAIObjects.setBackground(MapStatus.PROP_BACKGROUND_COLOR.get()); - } else if (addedObjects < maxAdd) { - mapWithAIObjects.setBackground(Color.GREEN); - } else if (addedObjects < 10L * maxAdd) { - mapWithAIObjects.setBackground(Color.YELLOW); - } else { - mapWithAIObjects.setBackground(Color.RED); - } - } - - @Override - public void destroy() { - statusLines.forEach(mapStatus -> mapStatus.remove(mapWithAIObjects)); - statusLines.clear(); - UndoRedoHandler.getInstance().removeCommandQueueListener(this); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java deleted file mode 100644 index 2e079202..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java +++ /dev/null @@ -1,201 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import org.openstreetmap.josm.data.osm.Tag; -import org.openstreetmap.josm.data.preferences.BooleanProperty; -import org.openstreetmap.josm.data.preferences.CachingProperty; -import org.openstreetmap.josm.data.preferences.DoubleProperty; -import org.openstreetmap.josm.data.preferences.IntegerProperty; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.spi.preferences.Config; - -/** - * Helper for MapWithAI preferences - */ -public final class MapWithAIPreferenceHelper { - private static final String AUTOSWITCHLAYERS = MapWithAIPlugin.NAME.concat(".autoswitchlayers"); - private static final String MERGEBUILDINGADDRESSES = MapWithAIPlugin.NAME.concat(".mergebuildingaddresses"); - private static final String MAXIMUMSELECTION = MapWithAIPlugin.NAME.concat(".maximumselection"); - private static final CachingProperty PROPERTY_DUPLICATE_NODE_DISTANCE = new DoubleProperty( - MapWithAIPlugin.NAME.concat(".duplicatenodedistance"), 0.6).cached(); - private static final IntegerProperty PROPERTY_MAXIMUM_SELECTION = new IntegerProperty(MAXIMUMSELECTION, 100); - private static final BooleanProperty PROPERTY_MERGEBUILDINGADDRESSES = new BooleanProperty(MERGEBUILDINGADDRESSES, - true); - private static final BooleanProperty PROPERTY_AUTOSWITCHLAYERS = new BooleanProperty(AUTOSWITCHLAYERS, true); - - private MapWithAIPreferenceHelper() { - // Hide the constructor - } - - /** - * The default maximum number of objects to add - * - * @return {@link MapWithAIPreferenceHelper#PROPERTY_MAXIMUM_SELECTION} default - */ - public static int getDefaultMaximumAddition() { - return PROPERTY_MAXIMUM_SELECTION.getDefaultValue(); - } - - /** - * Get the current MapWithAI urls - * - * @return A list of enabled MapWithAI urls (maps have source, parameters, - * enabled, and the url) - */ - public static Collection getMapWithAIUrl() { - MapWithAILayer layer = MapWithAIDataUtils.getLayer(false); - if (layer != null) { - if (!layer.getDownloadedInfo().isEmpty()) { - return layer.getDownloadedInfo(); - } else if (layer.getMapWithAIUrl() != null) { - return Collections.singleton(layer.getMapWithAIUrl()); - } - } - return MapWithAILayerInfo.getInstance().getLayers(); - } - - /** - * Get the maximum number of objects that can be added at one time - * - * @return The maximum selection. If 0, allow any number. - */ - public static int getMaximumAddition() { - final MapWithAILayer mapWithAILayer = MapWithAIDataUtils.getLayer(false); - Integer defaultReturn = PROPERTY_MAXIMUM_SELECTION.get(); - if ((mapWithAILayer != null) && (mapWithAILayer.getMaximumAddition() != null)) { - defaultReturn = mapWithAILayer.getMaximumAddition(); - } - return defaultReturn > getDefaultMaximumAddition() * 10 ? getDefaultMaximumAddition() * 10 : defaultReturn; - } - - /** - * Check if the user wants to merge buildings and addresses - * - * @return {@code true} if we want to automatically merge buildings with - * pre-existing addresses - */ - public static boolean isMergeBuildingAddress() { - return PROPERTY_MERGEBUILDINGADDRESSES.get(); - } - - /** - * Check if the user wants to switch layers automatically after adding data. - * - * @return {@code true} if we want to automatically switch layers - */ - public static boolean isSwitchLayers() { - final MapWithAILayer layer = MapWithAIDataUtils.getLayer(false); - boolean returnBoolean = PROPERTY_AUTOSWITCHLAYERS.get(); - if ((layer != null) && (layer.isSwitchLayers() != null)) { - returnBoolean = layer.isSwitchLayers(); - } - return returnBoolean; - } - - /** - * Add a MapWithAI url. If both boolean parameters are false, nothing happens. - * - * @param info The info to add - * @param enabled If it should be enabled - * @param permanent If it should be added permanently - */ - public static void setMapWithAIUrl(MapWithAIInfo info, boolean enabled, boolean permanent) { - if (permanent && enabled) { - MapWithAILayerInfo.getInstance().add(info); - MapWithAILayerInfo.getInstance().save(); - } else if (enabled && MapWithAIDataUtils.getLayer(false) != null) { - MapWithAIDataUtils.getLayer(false).setMapWithAIUrl(info); - } - } - - /** - * Set the maximum number of objects that can be added at one time. - * - * @param max The maximum number of objects to select (0 allows any number - * to be selected). - * @param permanent {@code true} if we want the setting to persist between - * sessions - */ - public static void setMaximumAddition(int max, boolean permanent) { - final MapWithAILayer mapWithAILayer = MapWithAIDataUtils.getLayer(false); - if (permanent) { - if (getDefaultMaximumAddition() == max) { - PROPERTY_MAXIMUM_SELECTION.remove(); - } else { - PROPERTY_MAXIMUM_SELECTION.put(max); - } - } else if (mapWithAILayer != null) { - mapWithAILayer.setMaximumAddition(max); - } - } - - /** - * Set whether or not a we switch from the MapWithAI layer to an OSM data layer - * - * @param selected true if we are going to switch layers - * @param permanent {@code true} if we want the setting to persist between - * sessions - */ - public static void setMergeBuildingAddress(boolean selected, boolean permanent) { - if (permanent) { - PROPERTY_MERGEBUILDINGADDRESSES.put(selected); - } - } - - /** - * Set whether or not a we switch from the MapWithAI layer to an OSM data layer - * - * @param selected true if we are going to switch layers - * @param permanent {@code true} if we want the setting to persist between - * sessions - */ - public static void setSwitchLayers(boolean selected, boolean permanent) { - final MapWithAILayer layer = MapWithAIDataUtils.getLayer(false); - if (permanent) { - PROPERTY_AUTOSWITCHLAYERS.put(selected); - } else if (layer != null) { - layer.setSwitchLayers(selected); - } - } - - /** - * Get the maximum distance for a node to be considered a duplicate - * - * @return The max distance between nodes for duplicates - */ - public static double getMaxNodeDistance() { - return PROPERTY_DUPLICATE_NODE_DISTANCE.get(); - } - - /** - * Get the tags to replace - * - * @return A map of tags to replacement tags (use {@link Tag#ofString} to parse) - */ - public static Map getReplacementTags() { - final Map defaultMap = Collections.emptyMap(); - final List> listOfMaps = Config.getPref() - .getListOfMaps(MapWithAIPlugin.NAME.concat(".replacementtags"), Collections.singletonList(defaultMap)); - return listOfMaps.isEmpty() ? defaultMap : listOfMaps.get(0); - } - - /** - * Set the tags to replace - * - * @param tagsToReplace set the tags to replace - */ - public static void setReplacementTags(Map tagsToReplace) { - final List> tags = tagsToReplace.isEmpty() ? null - : Collections.singletonList(new TreeMap<>(tagsToReplace)); - Config.getPref().putListOfMaps(MapWithAIPlugin.NAME.concat(".replacementtags"), tags); - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIRemoteControl.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIRemoteControl.java deleted file mode 100644 index 20888013..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIRemoteControl.java +++ /dev/null @@ -1,181 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.stream.Stream; - -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.layer.GpxLayer; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault; -import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; - -/** - * Download MapWithAI data using remote control - */ -public class MapWithAIRemoteControl extends RequestHandler.RawURLParseRequestHandler { - - private static final PermissionPrefWithDefault PERMISSION_PREF_WITH_DEFAULT = new PermissionPrefWithDefault( - MapWithAIPlugin.NAME.concat(".remote_control"), true, tr("MapWithAI")); - - private Bounds download; - private Bounds crop; - private Integer maxObj; - private Boolean switchLayer; - private String url; - private String source; - - private static final String MAX_OBJ = "max_obj"; - private static final String SWITCH_LAYER = "switch_layer"; - private static final String BBOX = "bbox"; - private static final String CROP_BBOX = "crop_bbox"; - private static final String URL_STRING = "url"; - private static final String SOURCE_STRING = "source"; - - @Override - protected void validateRequest() throws RequestHandlerBadRequestException { - if (args != null) { - try { - if (args.containsKey(BBOX)) { - download = parseBounds(args.get(BBOX)); - } - if (args.containsKey(CROP_BBOX)) { - crop = parseBounds(args.get(CROP_BBOX)); - } - if (args.containsKey(MAX_OBJ)) { - maxObj = Integer.parseInt(args.get(MAX_OBJ)); - } - if (args.containsKey(URL_STRING)) { - final String urlString = args.get(URL_STRING); - // Ensure the URL_STRING is valid - url = new URL(urlString).toString(); - } - if (args.containsKey(SOURCE_STRING)) { - source = args.get(SOURCE_STRING); - } - if (args.containsKey(SWITCH_LAYER)) { - switchLayer = Boolean.parseBoolean(args.get(SWITCH_LAYER)); - } - } catch (NumberFormatException e) { - throw new RequestHandlerBadRequestException("NumberFormatException (" + e.getMessage() + ')', e); - } catch (MalformedURLException e) { - throw new RequestHandlerBadRequestException("MalformedURLException: " + e.getMessage(), e); - } - } - } - - /** - * Parse a string of coordinates into bounds - * - * @param coordinates The coordinates to parse - * @return The new Bounds - * @throws RequestHandlerBadRequestException If there was something wrong with - * the coordinates - */ - private static Bounds parseBounds(String coordinates) throws RequestHandlerBadRequestException { - final Double[] coords = Stream.of(coordinates.split(",", -1)).map(Double::parseDouble).toArray(Double[]::new); - // min lat, min lon, max lat, max lon - final double minLat = Math.min(coords[1], coords[3]); - final double maxLat = Math.max(coords[1], coords[3]); - final double maxLon = Math.max(coords[0], coords[2]); - final double minLon = Math.min(coords[0], coords[2]); - final Bounds tBounds = new Bounds(minLat, minLon, maxLat, maxLon); - if (tBounds.isOutOfTheWorld() || tBounds.isCollapsed()) { - throw new RequestHandlerBadRequestException( - tr("Bad bbox: {0} (converted to {1})", coordinates, tBounds.toString())); - } - return tBounds; - } - - @Override - protected void handleRequest() throws RequestHandlerErrorException, RequestHandlerBadRequestException { - if (crop != null && crop.toBBox().isInWorld()) { - MainApplication.getLayerManager() - .addLayer(new GpxLayer(DetectTaskingManagerUtils.createTaskingManagerGpxData(crop), - DetectTaskingManagerUtils.MAPWITHAI_CROP_AREA)); - } - - final MapWithAILayer layer = MapWithAIDataUtils.getLayer(true); - - if (maxObj != null) { - layer.setMaximumAddition(maxObj); - } - if (url != null) { - // TODO make option for permanent url - String tSource = source == null ? url : source; - MapWithAIInfo info = new MapWithAIInfo(tSource, url); - layer.setMapWithAIUrl(info); - } - if (switchLayer != null) { - layer.setSwitchLayers(switchLayer); - } - - if (download != null && download.toBBox().isInWorld()) { - MapWithAIDataUtils.getMapWithAIData(layer, download); - } else if (MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream() - .anyMatch(tLayer -> !(tLayer instanceof MapWithAILayer))) { - MapWithAIDataUtils.getMapWithAIData(layer); - } else if (crop != null && crop.toBBox().isInWorld()) { - MapWithAIDataUtils.getMapWithAIData(layer, crop); - } - } - - @Override - public String getPermissionMessage() { - final String br = "
"; - final StringBuilder sb = new StringBuilder(); - sb.append(tr("Remote Control has been asked to load data from the API.")).append(" (").append(url).append(')') - .append(br).append(tr("{0} will ", MapWithAIPlugin.NAME)); - if (Boolean.FALSE.equals(switchLayer)) { - sb.append(tr("not ")); - } - sb.append(tr("automatically switch layers.")).append(br); - if (download != null) { - sb.append(tr("We will download data in ")).append(download.toBBox().toStringCSV(",")).append(br); - } - if (crop != null) { - sb.append(tr("We will crop the data to ")).append(crop.toBBox().toStringCSV(",")).append(br); - } - sb.append(tr("There is a maximum addition of {0} objects at one time", maxObj)); - return sb.toString(); - - } - - @Override - public PermissionPrefWithDefault getPermissionPref() { - return PERMISSION_PREF_WITH_DEFAULT; - } - - @Override - public String[] getMandatoryParams() { - return new String[] {}; - } - - @Override - public String[] getOptionalParams() { - return new String[] { BBOX, URL_STRING, MAX_OBJ, SWITCH_LAYER, CROP_BBOX }; - } - - @Override - public String getUsage() { - return tr("downloads {0} data", MapWithAIPlugin.NAME); - } - - @Override - public String[] getUsageExamples() { - return new String[] { "/mapwithai", "/mapwithai?bbox=-108.4625421,39.0621223,-108.4594728,39.0633059", - "/mapwithai?url=https://www.mapwith.ai/maps/ml_roads?conflate_with_osm=true" - + "&theme=ml_road_vector&collaborator=josm" - + "&token=ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m" - + "&hash=ASawRla3rBcwEjY4HIY&bbox={bbox}", - "/mapwithai?bbox=-108.4625421,39.0621223,-108.4594728,39.0633059&max_obj=1", - "/mapwithai?bbox=-108.4625421,39.0621223,-108.4594728,39.0633059&switch_layer=false", - "/mapwithai?crop_bbox=-108.4625421,39.0621223,-108.4594728,39.0633059" }; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIUploadHook.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIUploadHook.java deleted file mode 100644 index 43f1648d..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIUploadHook.java +++ /dev/null @@ -1,74 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.actions.UploadAction; -import org.openstreetmap.josm.actions.upload.UploadHook; -import org.openstreetmap.josm.plugins.PluginInformation; -import org.openstreetmap.josm.tools.Destroyable; - -/** - * Add information that is useful for QC/debugging to OSM changesets. - * - * @author Taylor Smock - */ -public class MapWithAIUploadHook implements UploadHook, Destroyable { - private final String version; - - /** - * Create the upload hook - * - * @param info The info to get version information from - */ - public MapWithAIUploadHook(PluginInformation info) { - version = info.localversion; - UploadAction.registerUploadHook(this); - } - - @Override - public void modifyChangesetTags(Map tags) { - final Long addedObjects = MapWithAIDataUtils.getAddedObjects(); - if (addedObjects != 0) { - tags.put("mapwithai", addedObjects.toString()); - final StringBuilder sb = new StringBuilder(); - sb.append("version=").append(version); - if (MapWithAIPreferenceHelper.getMaximumAddition() != MapWithAIPreferenceHelper - .getDefaultMaximumAddition()) { - sb.append(";maxadd=").append(MapWithAIPreferenceHelper.getMaximumAddition()); - } - if (DetectTaskingManagerUtils.hasTaskingManagerLayer()) { - sb.append(";task=") - .append(DetectTaskingManagerUtils.getTaskingManagerBounds().toBBox().toStringCSV(",")); - } - if (!MapWithAIPreferenceHelper.getMapWithAIUrl().isEmpty()) { - sb.append(";url_ids=") - .append(String.join(";url_ids=", - MapWithAIPreferenceHelper.getMapWithAIUrl().stream() - .map(i -> i.getId() == null ? i.getUrl() : i.getId()).filter(Objects::nonNull) - .collect(Collectors.joining(",")))); - } - String mapwithaiOptions = sb.toString(); - if (mapwithaiOptions.length() > 255) { - tags.put("mapwithai:options", mapwithaiOptions.substring(0, 255)); - int start = 255; - int i = 1; - while (start < mapwithaiOptions.length()) { - tags.put("mapwithai:options:" + i, mapwithaiOptions.substring(start, - start + 255 < mapwithaiOptions.length() ? start + 255 : mapwithaiOptions.length())); - start = start + 255; - i++; - } - } else { - tags.put("mapwithai:options", mapwithaiOptions); - } - } - } - - @Override - public void destroy() { - UploadAction.unregisterUploadHook(this); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MergeDuplicateWaysAction.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MergeDuplicateWaysAction.java deleted file mode 100644 index a57b59ea..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MergeDuplicateWaysAction.java +++ /dev/null @@ -1,72 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import static org.openstreetmap.josm.gui.help.HelpUtil.ht; -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.util.ArrayList; -import java.util.List; - -import org.openstreetmap.josm.actions.JosmAction; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.data.UndoRedoHandler; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.commands.MergeDuplicateWays; -import org.openstreetmap.josm.tools.Shortcut; - -/** - * An action that attempts to merge duplicate ways - * - * @author Taylor Smock - */ -public class MergeDuplicateWaysAction extends JosmAction { - private static final long serialVersionUID = 8971004636405132635L; - private static final String DESCRIPTION = "Attempt to merge potential duplicate ways"; - /** - * If there are 2 ways, we directly compare them to see if they are duplicates - */ - private static final int COMPARE_WAYS_NUMBER = 2; - - /** - * Create a new action - */ - public MergeDuplicateWaysAction() { - super(tr("{0}: ".concat(DESCRIPTION), MapWithAIPlugin.NAME), "mapwithai", tr(DESCRIPTION), - Shortcut.registerShortcut("data:attemptmergeway", tr(DESCRIPTION), KeyEvent.VK_EXCLAMATION_MARK, - Shortcut.ALT_CTRL_SHIFT), - true, "mapwithai:attemptmergeway", true); - setHelpId(ht("Plugin/MapWithAI")); - } - - @Override - public void actionPerformed(ActionEvent e) { - if (MainApplication.getLayerManager().getActiveDataSet() != null) { - final List ways = new ArrayList<>( - MainApplication.getLayerManager().getActiveDataSet().getSelectedWays()); - Command command = null; - int i = 0; - do { - if (ways.size() == COMPARE_WAYS_NUMBER) { - command = new MergeDuplicateWays(ways.get(0), ways.get(1)); - } else if (ways.size() == 1) { - command = new MergeDuplicateWays(ways.get(0)); - } else if (ways.isEmpty()) { - command = new MergeDuplicateWays(MainApplication.getLayerManager().getActiveDataSet()); - } - if (command != null) { - UndoRedoHandler.getInstance().add(command); - i++; - } - } while ((command != null) && (i < 1)); - } - } - - @Override - public void updateEnabledState() { - setEnabled(MainApplication.getLayerManager().getActiveDataSet() != null); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/TileXYZ.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/TileXYZ.java deleted file mode 100644 index 1e6df34d..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/TileXYZ.java +++ /dev/null @@ -1,121 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.backend; - -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import org.openstreetmap.josm.data.Bounds; - -/** - * Create a tile - * - * @param x The x coordinate of the tile - * @param y The y coordinate of the tile - * @param z The zoom level - */ -record TileXYZ(int x, int y, int z) { - /** - * Checks to see if the given bounds are functionally equal to this tile - * - * @param left left - * @param bottom bottom - * @param right right - * @param top top - */ - boolean checkBounds(double left, double bottom, double right, double top) { - final var thisLeft = xToLongitude(this.x, this.z); - final var thisRight = xToLongitude(this.x + 1, this.z); - final var thisBottom = yToLatitude(this.y + 1, this.z); - final var thisTop = yToLatitude(this.y, this.z); - return equalsEpsilon(thisLeft, left, this.z) && equalsEpsilon(thisRight, right, this.z) - && equalsEpsilon(thisBottom, bottom, this.z) && equalsEpsilon(thisTop, top, this.z); - } - - private static boolean equalsEpsilon(double first, double second, int z) { - // 0.1% of tile size is considered to be "equal" - final var maxDiff = (360 / Math.pow(2, z)) / 1000; - final var diff = Math.abs(first - second); - return diff <= maxDiff; - } - - private static double xToLongitude(int x, int z) { - return (x / Math.pow(2, z)) * 360 - 180; - } - - private static double yToLatitude(int y, int z) { - var t = Math.PI - 2 * Math.PI * y / Math.pow(2, z); - return 180 / Math.PI * Math.atan((Math.exp(t) - Math.exp(-t)) / 2); - } - - /** - * Convert bounds to tiles - * - * @param zoom The zoom level to use - * @param bounds The bounds to convert to tiles - * @return A stream of tiles for the bounds at the given zoom level - */ - static Stream tilesFromBBox(int zoom, Bounds bounds) { - final var left = bounds.getMinLon(); - final var bottom = bounds.getMinLat(); - final var right = bounds.getMaxLon(); - final var top = bounds.getMaxLat(); - final var tile1 = tileFromLatLonZoom(left, bottom, zoom); - final var tile2 = tileFromLatLonZoom(right, top, zoom); - return IntStream.rangeClosed(tile1.x, tile2.x) - .mapToObj(x -> IntStream.rangeClosed(tile2.y, tile1.y).mapToObj(y -> new TileXYZ(x, y, zoom))) - .flatMap(stream -> stream); - } - - /** - * Checks to see if the given bounds are functionally equal to this tile - * - * @param left left lon - * @param bottom bottom lat - * @param right right lon - * @param top top lat - */ - static TileXYZ tileFromBBox(double left, double bottom, double right, double top) { - var zoom = 18; - while (zoom > 0) { - final var tile1 = tileFromLatLonZoom(left, bottom, zoom); - final var tile2 = tileFromLatLonZoom(right, top, zoom); - if (tile1.equals(tile2)) { - return tile1; - } else if (tile1.checkBounds(left, bottom, right, top)) { - return tile1; - } else if (tile2.checkBounds(left, bottom, right, top)) { - return tile2; - // Just in case the coordinates are _barely_ in other tiles and not the "common" - // tile - } else if (Math.abs(tile1.x() - tile2.x()) <= 2 && Math.abs(tile1.y() - tile2.y()) <= 2) { - final var tileT = new TileXYZ((tile1.x() + tile2.x()) / 2, (tile1.y() + tile2.y()) / 2, zoom); - if (tileT.checkBounds(left, bottom, right, top)) { - return tileT; - } - } - zoom--; - } - return new TileXYZ(0, 0, 0); - } - - static TileXYZ tileFromLatLonZoom(double lon, double lat, int zoom) { - var xCoordinate = Math.toIntExact(Math.round(Math.floor(Math.pow(2, zoom) * (180 + lon) / 360))); - var yCoordinate = Math.toIntExact(Math.round(Math.floor(Math.pow(2, zoom) - * (1 - (Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI)) / 2))); - return new TileXYZ(xCoordinate, yCoordinate, zoom); - } - - /** - * Extends a bounds object to contain this tile - * - * @param currentBounds The bounds to extend - */ - void expandBounds(Bounds currentBounds) { - final var thisLeft = xToLongitude(this.x, this.z); - final var thisRight = xToLongitude(this.x + 1, this.z); - final var thisBottom = yToLatitude(this.y + 1, this.z); - final var thisTop = yToLatitude(this.y, this.z); - currentBounds.extend(thisBottom, thisLeft); - currentBounds.extend(thisTop, thisRight); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/AbstractConflationCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/AbstractConflationCommand.java deleted file mode 100644 index df11e73b..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/AbstractConflationCommand.java +++ /dev/null @@ -1,194 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.awt.GraphicsEnvironment; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.TreeMap; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.OsmPrimitiveType; -import org.openstreetmap.josm.data.osm.PrimitiveId; -import org.openstreetmap.josm.data.osm.SimplePrimitiveId; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.io.DownloadPrimitivesTask; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.gui.progress.NullProgressMonitor; -import org.openstreetmap.josm.gui.progress.ProgressMonitor; -import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor; -import org.openstreetmap.josm.tools.Pair; - -/** - * This is an abstract class for conflation commands. This class is primarily - * used in {@link CreateConnectionsCommand#createConnections}. - * - * @author Taylor Smock - * - */ -public abstract class AbstractConflationCommand extends Command { - protected Collection possiblyAffectedPrimitives; - - /** - * Creates a new command in the context of a specific data set, without data - * layer - * - * @param data the data set. Must not be null. - */ - protected AbstractConflationCommand(DataSet data) { - super(data); - } - - @Override - public void fillModifiedData(Collection modified, Collection deleted, - Collection added) { - // Do nothing -- the sequence commands should take care of it. - } - - /** - * Only return Node/Way/Relation here. It can be any combination. - * - * @return The types of primitive that the command is interested in - */ - public abstract Collection> getInterestedTypes(); - - /** - * Return the key that the command uses to perform conflation. For example, - * `conn` or `dupe`. - * - * @return The key that the command is interested in - */ - public abstract String getKey(); - - /** - * Get the actual command to run. This should not be normally overriden by - * subclasses. Override {@link AbstractConflationCommand#getRealCommand} - * instead. - * - * @param primitives The primitives to run the command on - * @return The command that will be run (may be {@code null}) - */ - public Command getCommand(Collection primitives) { - possiblyAffectedPrimitives = new HashSet<>(primitives); - return getRealCommand(); - } - - /** - * A command that performs the conflation steps. - * - * @return The command to do whatever is required for the result - */ - public abstract Command getRealCommand(); - - /** - * Get the primitives from a dataset with specified ids - * - * @param dataSet The dataset holding the primitives (hopefully) - * @param ids The ids formated like - * n<NUMBER>,r<NUMBER>,w<NUMBER> - * @return The primitives that the ids point to, if in the dataset. - */ - public static OsmPrimitive[] getPrimitives(DataSet dataSet, String ids) { - Objects.requireNonNull(dataSet, tr("DataSet cannot be null")); - Objects.requireNonNull(ids, tr("The ids string cannot be null")); - final Map> missingPrimitives = new TreeMap<>(); - final String[] connections = ids.split(",", -1); - final OsmPrimitive[] primitiveConnections = new OsmPrimitive[connections.length]; - for (int i = 0; i < connections.length; i++) { - final String member = connections[i]; - try { - SimplePrimitiveId primitiveId = SimplePrimitiveId.fromString(member); - primitiveConnections[i] = dataSet.getPrimitiveById(primitiveId); - if (primitiveConnections[i] == null) { - missingPrimitives.put(i, new Pair<>(primitiveId.getUniqueId(), primitiveId.getType())); - } - } catch (IllegalArgumentException e) { - // Assume someone fiddled with the tag if the pattern doesn't match. - if (!e.getMessage().contains("n|node|w|way|r|rel|relation")) { - throw e; - } - } - } - obtainMissingPrimitives(dataSet, primitiveConnections, missingPrimitives); - return primitiveConnections; - } - - private static void obtainMissingPrimitives(DataSet dataSet, OsmPrimitive[] primitiveConnections, - Map> missingPrimitives) { - if (!missingPrimitives.isEmpty()) { - final Map ids = missingPrimitives.entrySet().stream().collect(Collectors - .toMap(entry -> new SimplePrimitiveId(entry.getValue().a, entry.getValue().b), Map.Entry::getKey)); - final List toFetch = new ArrayList<>(ids.keySet()); - final Optional optionalLayer = MainApplication.getLayerManager() - .getLayersOfType(OsmDataLayer.class).stream().filter(layer -> layer.getDataSet().equals(dataSet)) - .findFirst(); - - final String generatedLayerName = "EvKlVarShAiAllsM generated layer"; - final OsmDataLayer layer = optionalLayer - .orElseGet(() -> new OsmDataLayer(dataSet, generatedLayerName, null)); - - final ProgressMonitor monitor; - if (GraphicsEnvironment.isHeadless()) { - monitor = NullProgressMonitor.INSTANCE; - } else { - monitor = new PleaseWaitProgressMonitor(tr("Downloading additional OsmPrimitives")); - } - final DownloadPrimitivesTask downloadPrimitivesTask = new DownloadPrimitivesTask(layer, toFetch, true, - monitor); - downloadPrimitivesTask.run(); - for (final Map.Entry entry : ids.entrySet()) { - final int index = entry.getValue(); - final OsmPrimitive primitive = dataSet.getPrimitiveById(entry.getKey()); - primitiveConnections[index] = primitive; - } - - if (generatedLayerName.equals(layer.getName())) { - layer.destroy(); - } - } - } - - @Override - public Collection getParticipatingPrimitives() { - // This is used for debugging. Default to anything that might be affected. - return Collections.unmodifiableCollection(this.possiblyAffectedPrimitives); - } - - /** - * Use this to ensure that something that cannot be undone without errors isn't - * undone. - * - * @return true if the command should show as a separate command in the - * undo/redo lists - */ - public abstract boolean allowUndo(); - - /** - * `conn` and `dupe` should not exist in OSM, but `addr:street` should. This is - * used in a validation test. - * - * @return {@code true} if the key should not exist in OpenStreetMap - */ - public abstract boolean keyShouldNotExistInOSM(); - - /** - * If another command conflicts with this command, it should be returned here. - * For example, if one command adds an addr node to a building, and another - * command does the reverse, they conflict. - * - * @return Conflation commands that conflict with this conflation command - */ - public Collection> conflictedCommands() { - return Collections.emptyList(); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/AddNodeToWayCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/AddNodeToWayCommand.java deleted file mode 100644 index 04956b8f..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/AddNodeToWayCommand.java +++ /dev/null @@ -1,141 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.command.ChangeCommand; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.data.osm.IWaySegment; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIPreferenceHelper; -import org.openstreetmap.josm.tools.Geometry; -import org.openstreetmap.josm.tools.Logging; - -/** - * Add a node to a way - */ -public class AddNodeToWayCommand extends Command { - private final Node toAddNode; - private final Way way; - private final Node firstNode; - private final Node secondNode; - private Command changeCommand; - - /** - * Add a node to a way in an undoable manner - * - * @param toAddNode The node to add - * @param way The way to add the node to - * @param first The node that comes before the node to add - * @param second The node that comes after the node to add - */ - public AddNodeToWayCommand(Node toAddNode, Way way, Node first, Node second) { - super(way.getDataSet()); - this.toAddNode = toAddNode; - this.way = way; - this.firstNode = first; - this.secondNode = second; - } - - @Override - public boolean executeCommand() { - int index = Integer.MIN_VALUE; - try { - // IWaySegment#forNodePair throws an IllegalArgumentException when the node pair - // doesn't exist as a segment in the way. - IWaySegment.forNodePair(getWay(), getFirstNode(), getSecondNode()); - index = Math.max(getWay().getNodes().indexOf(getFirstNode()), getWay().getNodes().indexOf(getSecondNode())); - } catch (IllegalArgumentException e) { - Logging.trace(e); - // OK, someone has added a node between the two nodes since calculation - Way tWay = new Way(); - tWay.setNodes(Arrays.asList(getFirstNode(), getSecondNode())); - List relevantNodes = getWay().getNodes().stream() - .filter(node -> Geometry.getDistance(tWay, node) < MapWithAIPreferenceHelper.getMaxNodeDistance()) - .collect(Collectors.toList()); - for (int i = 0; i < relevantNodes.size() - 1; i++) { - Way tWay2 = new Way(); - tWay2.setNodes(Arrays.asList(relevantNodes.get(i), relevantNodes.get(i + 1))); - if (Geometry.getDistance(tWay2, getToAddNode()) < MapWithAIPreferenceHelper.getMaxNodeDistance()) { - index = Math.max(way.getNodes().indexOf(tWay2.firstNode()), - way.getNodes().indexOf(tWay2.lastNode())); - } - } - } - if (index != Integer.MIN_VALUE && changeCommand == null) { - Way tWay = new Way(getWay()); - tWay.addNode(index, getToAddNode()); - changeCommand = new ChangeCommand(getWay(), tWay); - } - if (changeCommand != null) { - changeCommand.executeCommand(); - } - return true; - } - - @Override - public void undoCommand() { - if (changeCommand != null) { - changeCommand.undoCommand(); - } - } - - @Override - public String getDescriptionText() { - return tr("Add node to way"); - } - - @Override - public void fillModifiedData(Collection modified, Collection deleted, - Collection added) { - modified.addAll(Arrays.asList(getToAddNode(), getWay())); - } - - @Override - public Collection getParticipatingPrimitives() { - return changeCommand.getParticipatingPrimitives(); - } - - /** - * Get the node that will be added to a way - * - * @return {@link Node} to add to {@link Way} - */ - public Node getToAddNode() { - return toAddNode; - } - - /** - * Get the way that we are modifying - * - * @return {@link Way} that we are adding a {@link Node} to - */ - public Way getWay() { - return way; - } - - /** - * Get the node that we are adding our node after - * - * @return {@link Node} that we are adding another {@link Node} after. - */ - public Node getFirstNode() { - return firstNode; - } - - /** - * Get the node that we are adding our node before - * - * @return {@link Node} that we are adding another {@link Node} before. - */ - public Node getSecondNode() { - return secondNode; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/AlreadyConflatedCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/AlreadyConflatedCommand.java deleted file mode 100644 index 77b84035..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/AlreadyConflatedCommand.java +++ /dev/null @@ -1,66 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.command.ChangePropertyCommand; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.PreConflatedDataUtils; - -/** - * All this currently does is remove - * {@link PreConflatedDataUtils#CONFLATED_KEY}. - * - * @author Taylor Smock - * - */ -public class AlreadyConflatedCommand extends AbstractConflationCommand { - - public AlreadyConflatedCommand(DataSet data) { - super(data); - } - - @Override - public String getDescriptionText() { - return tr("Remove key for already conflated data"); - } - - @Override - public Collection> getInterestedTypes() { - return Arrays.asList(Node.class, Way.class, Relation.class); - } - - @Override - public String getKey() { - return PreConflatedDataUtils.getConflatedKey(); - } - - @Override - public Command getRealCommand() { - List commands = possiblyAffectedPrimitives.stream().filter(p -> p.hasTag(getKey())) - .map(k -> new ChangePropertyCommand(k, getKey(), "")).collect(Collectors.toList()); - return commands.isEmpty() ? null : SequenceCommand.wrapIfNeeded(getDescriptionText(), commands); - } - - @Override - public boolean allowUndo() { - return true; - } - - @Override - public boolean keyShouldNotExistInOSM() { - return true; - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/ConnectedCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/ConnectedCommand.java deleted file mode 100644 index 13bba444..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/ConnectedCommand.java +++ /dev/null @@ -1,122 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.command.ChangeCommand; -import org.openstreetmap.josm.command.ChangePropertyCommand; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.data.projection.ProjectionRegistry; -import org.openstreetmap.josm.tools.Geometry; -import org.openstreetmap.josm.tools.Logging; - -/** - * Connect a way to another way (in between nodes) - */ -public class ConnectedCommand extends AbstractConflationCommand { - public static final String KEY = "conn"; - - public ConnectedCommand(DataSet data) { - super(data); - } - - @Override - public String getDescriptionText() { - return tr("Connect nodes to ways"); - } - - /** - * Add a node to a way - * - * @param toAddNode The node to add - * @param way The way to add the node to - * @param first The first node in a waysegment (the node is between this and - * the second node) - * @param second The second node in a waysegment - * @return Commands to add a node to a way, or null if it won't be done - */ - public static List addNodesToWay(Node toAddNode, Way way, Node first, Node second) { - List tCommands = new ArrayList<>(); - final Way tWay = new Way(); - final List ways = toAddNode.getReferrers().stream().filter(w -> !w.equals(way)) - .filter(Way.class::isInstance).map(Way.class::cast).collect(Collectors.toList()); - tWay.addNode(first); - tWay.addNode(second); - final double distance = Geometry.getDistanceWayNode(tWay, toAddNode); - if (distance < 5) { - if (!ways.isEmpty()) { - int index = ways.get(0).getNodes().indexOf(toAddNode); - final Node node4 = index == 0 ? ways.get(0).getNode(1) : ways.get(0).getNode(index - 1); - final Node tNode = new Node(toAddNode); - tNode.setCoor(ProjectionRegistry.getProjection().eastNorth2latlon(Geometry.getLineLineIntersection( - first.getEastNorth(), second.getEastNorth(), toAddNode.getEastNorth(), node4.getEastNorth()))); - tCommands.add(new ChangeCommand(toAddNode, tNode)); - } - tCommands.add(new AddNodeToWayCommand(toAddNode, way, first, second)); - } - return tCommands; - } - - private static List connectedCommand(DataSet dataSet, Node node) { - final List commands = new ArrayList<>(); - final OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get(KEY)); - for (int i = 0; i < primitiveConnections.length / 3; i++) { - if (primitiveConnections[i] instanceof Way && primitiveConnections[i + 1] instanceof Node - && primitiveConnections[i + 2] instanceof Node) { - final List addNodesToWayCommand = addNodesToWay(node, (Way) primitiveConnections[i], - (Node) primitiveConnections[i + 1], (Node) primitiveConnections[i + 2]); - commands.addAll(addNodesToWayCommand); - } else { - Logging.error("MapWithAI: Cannot create connections ({0}: {1}, {2}: {3}, {4}: {5})", i, - primitiveConnections[i] == null ? null : primitiveConnections[i].getClass(), i + 1, - primitiveConnections[i + 1] == null ? null : primitiveConnections[i + 1].getClass(), i + 2, - primitiveConnections[i + 2] == null ? null : primitiveConnections[i + 2].getClass()); - } - } - commands.add(new ChangePropertyCommand(node, KEY, null)); - return commands; - } - - @Override - public Collection> getInterestedTypes() { - return Collections.singletonList(Node.class); - } - - @Override - public String getKey() { - return KEY; - } - - @Override - public Command getRealCommand() { - final List commands = new ArrayList<>(); - possiblyAffectedPrimitives.stream().filter(Node.class::isInstance).map(Node.class::cast) - .forEach(node -> commands.addAll(connectedCommand(getAffectedDataSet(), node))); - Command returnCommand = null; - if (!commands.isEmpty()) { - returnCommand = new SequenceCommand(getDescriptionText(), commands); - } - return returnCommand; - } - - @Override - public boolean allowUndo() { - return false; - } - - @Override - public boolean keyShouldNotExistInOSM() { - return true; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommand.java deleted file mode 100644 index 34f322ee..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommand.java +++ /dev/null @@ -1,186 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.UndoRedoHandler; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.PrimitiveData; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.commands.cleanup.MissingConnectionTags; -import org.openstreetmap.josm.plugins.mapwithai.commands.cleanup.OverNodedWays; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Utils; - -/** - * Create connections - */ -public class CreateConnectionsCommand extends Command { - private final Collection primitives; - private Command command; - private Command undoCommands; - private static final LinkedHashSet> CONFLATION_COMMANDS = new LinkedHashSet<>(); - static { - CONFLATION_COMMANDS.add(MissingConnectionTags.class); - CONFLATION_COMMANDS.add(ConnectedCommand.class); - CONFLATION_COMMANDS.add(DuplicateCommand.class); - CONFLATION_COMMANDS.add(MergeAddressBuildings.class); - CONFLATION_COMMANDS.add(MergeBuildingAddress.class); - CONFLATION_COMMANDS.add(OverNodedWays.class); - CONFLATION_COMMANDS.add(AlreadyConflatedCommand.class); - } - - /** - * Create a new command - * - * @param data The dataset - * @param primitives The primitives to connect - */ - public CreateConnectionsCommand(DataSet data, Collection primitives) { - super(data); - this.primitives = primitives; - } - - @Override - public boolean executeCommand() { - if (command == null) { - List commands = createConnections(getAffectedDataSet(), primitives); - command = commands.get(0); - undoCommands = commands.get(1); - } - if (command != null) { - command.executeCommand(); - } - if (undoCommands != null && !UndoRedoHandler.getInstance().getUndoCommands().contains(undoCommands)) { - GuiHelper.runInEDTAndWait(() -> UndoRedoHandler.getInstance().add(undoCommands)); - } - return true; - } - - @Override - public void undoCommand() { - if (command != null) { - command.undoCommand(); - } - } - - /** - * Create connections based off of current MapWithAI syntax - * - * @param dataSet The {@link DataSet} that should have the primitives we are - * connecting to - * @param collection The primitives with connection information (currently only - * checks Nodes) - * @return A list {@link Command} to create connections with (first is one that - * can be folded into other commands, second is one that should be - * undoable individually) - */ - public static List createConnections(DataSet dataSet, Collection collection) { - final List permanent = new ArrayList<>(); - final List undoable = new ArrayList<>(); - List> runCommands = new ArrayList<>(); - for (final Class abstractCommandClass : getConflationCommands()) { - final AbstractConflationCommand abstractCommand; - try { - abstractCommand = abstractCommandClass.getConstructor(DataSet.class).newInstance(dataSet); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | NoSuchMethodException | SecurityException e) { - Logging.debug(e); - continue; - } - // If there are conflicting commands, don't add it. - if (runCommands.stream().anyMatch(c -> abstractCommand.conflictedCommands().contains(c))) { - continue; - } - final Collection realPrimitives = collection.stream().map(dataSet::getPrimitiveById) - .filter(Objects::nonNull).collect(Collectors.toList()); - final Collection tPrimitives = new TreeSet<>(); - abstractCommand.getInterestedTypes() - .forEach(clazz -> tPrimitives.addAll(Utils.filteredCollection(realPrimitives, clazz))); - - final Command actualCommand = abstractCommand.getCommand( - tPrimitives.stream().filter(prim -> prim.hasKey(abstractCommand.getKey()) && !prim.isDeleted()) - .collect(Collectors.toList())); - if (Objects.nonNull(actualCommand)) { - if (abstractCommand.allowUndo()) { - undoable.add(actualCommand); - } else { - permanent.add(actualCommand); - } - runCommands.add(abstractCommand.getClass()); - } - } - - Command permanentCommand = permanent.isEmpty() ? null - : SequenceCommand.wrapIfNeeded(getRealDescriptionText(), permanent); - Command undoCommand = undoable.isEmpty() ? null - : SequenceCommand.wrapIfNeeded(getRealDescriptionText(), undoable); - - return Arrays.asList(permanentCommand, undoCommand); - } - - @Override - public String getDescriptionText() { - return getRealDescriptionText(); - } - - private static String getRealDescriptionText() { - return tr("Create connections from {0} data", MapWithAIPlugin.NAME); - } - - @Override - public void fillModifiedData(Collection modified, Collection deleted, - Collection added) { - command.fillModifiedData(modified, deleted, added); - } - - @Override - public Collection getParticipatingPrimitives() { - return command.getParticipatingPrimitives(); - } - - /** - * Add third-party commands that are run when conflating data. - * - * @param command A command to run when copying data from the MapWithAI layer - */ - public static void addConflationCommand(Class command) { - CONFLATION_COMMANDS.add(command); - } - - /** - * Get the commands to run when conflating data. - * - * @return A set of commands to run when copying data from the MapWithAI layer - */ - public static Set> getConflationCommands() { - return Collections.unmodifiableSet(CONFLATION_COMMANDS); - } - - /** - * Remove a command that runs when conflating data. - * - * @param command The command class to remove - * @return {@code true} if the conflation command was removed and was present - * @see List#remove - */ - public static boolean removeConflationCommand(Class command) { - return CONFLATION_COMMANDS.remove(command); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/DuplicateCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/DuplicateCommand.java deleted file mode 100644 index 5e572cc8..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/DuplicateCommand.java +++ /dev/null @@ -1,125 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.actions.MergeNodesAction; -import org.openstreetmap.josm.command.ChangePropertyCommand; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Utils; - -/** - * Connect duplicate nodes - */ -public class DuplicateCommand extends AbstractConflationCommand { - public static final String KEY = "dupe"; - - public DuplicateCommand(DataSet data) { - super(data); - } - - private static List duplicateNode(DataSet dataSet, Node node) { - final OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get(KEY)); - if (primitiveConnections.length != 1) { - Logging.error("{0}: {3} connection connected to more than one node? ({3}={1})", MapWithAIPlugin.NAME, - node.get(KEY), KEY); - } - - final List commands = new ArrayList<>(); - if (primitiveConnections[0] instanceof Node && !primitiveConnections[0].isDeleted()) { - final Node replaceNode = (Node) primitiveConnections[0]; - final Command tCommand = replaceNode(node, replaceNode); - if (tCommand != null) { - commands.add(tCommand); - if (replaceNode.hasKey(KEY)) { - final String key = replaceNode.get(KEY); - commands.add(new ChangePropertyCommand(replaceNode, KEY, key)); - } else { - replaceNode.put(KEY, "empty_value"); // This is needed to actually have a command. - commands.add(new ChangePropertyCommand(replaceNode, KEY, null)); - replaceNode.remove(KEY); - } - } - } - if (commands.isEmpty()) { - commands.add(new ChangePropertyCommand(node, KEY, null)); - } - return commands; - } - - /** - * Replace nodes that are in the same location - * - * @param original The original node (the one to replace) - * @param newNode The node that is replacing the original node - * @return A command that replaces the node, or null if they are not at the same - * location. - */ - public static Command replaceNode(Node original, Node newNode) { - Command tCommand = null; - if (original.equalsEpsilon(newNode)) { - tCommand = MergeNodesAction.mergeNodes(Collections.singletonList(original), newNode, newNode); - } - return tCommand; - } - - @Override - public String getDescriptionText() { - return tr("Remove duplicated nodes"); - } - - @Override - public Collection> getInterestedTypes() { - return Collections.singletonList(Node.class); - } - - @Override - public String getKey() { - return KEY; - } - - @Override - public Command getRealCommand() { - final List commands = new ArrayList<>(); - for (Node tNode : Utils.filteredCollection(possiblyAffectedPrimitives, Node.class).stream().distinct() - .filter(node -> node.hasKey(KEY)).collect(Collectors.toList())) { - List tCommands = duplicateNode(getAffectedDataSet(), tNode); - // We have to execute the command to avoid duplicating the command later. Undo - // occurs later, so that the state doesn't actually change. - tCommands.forEach(Command::executeCommand); - commands.addAll(tCommands); - } - Collections.reverse(commands); - commands.forEach(Command::undoCommand); - Collections.reverse(commands); - Command returnCommand = null; - if (commands.size() == 1) { - returnCommand = commands.get(0); - } else if (!commands.isEmpty()) { - returnCommand = new SequenceCommand(getDescriptionText(), commands); - } - return returnCommand; - } - - @Override - public boolean allowUndo() { - return false; - } - - @Override - public boolean keyShouldNotExistInOSM() { - return true; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MapWithAIAddCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MapWithAIAddCommand.java deleted file mode 100644 index 01b0d943..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MapWithAIAddCommand.java +++ /dev/null @@ -1,215 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.locks.Lock; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.UndoRedoHandler; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.PrimitiveData; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.backend.GetDataRunnable; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAILayer; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Pair; -import org.openstreetmap.josm.tools.Utils; - -/** - * Add data from the MapWithAI layer to the OSM data layer - */ -public class MapWithAIAddCommand extends Command implements Runnable { - private final DataSet editable; - private final DataSet mapWithAI; - private final Collection primitives; - Command command; - private Lock lock; - final Map sources; - - /** - * Add primitives from MapWithAI to the OSM data layer - * - * @param mapWithAILayer The MapWithAI layer - * @param editLayer The OSM layer - * @param selection The primitives to add from MapWithAI - */ - public MapWithAIAddCommand(MapWithAILayer mapWithAILayer, OsmDataLayer editLayer, - Collection selection) { - this(mapWithAILayer.getDataSet(), editLayer.getDataSet(), selection); - lock = mapWithAILayer.getLock(); - } - - /** - * Add primitives from MapWithAI to the OSM data layer - * - * @param mapWithAI The MapWithAI dataset - * @param editable The OSM dataset - * @param selection The primitives to add from MapWithAI - */ - public MapWithAIAddCommand(DataSet mapWithAI, DataSet editable, Collection selection) { - super(editable); - this.mapWithAI = mapWithAI; - this.editable = editable; - Collection nodeReferrers = Utils.filteredCollection(selection, Node.class).stream().map(Node::getReferrers) - .flatMap(List::stream).filter(Way.class::isInstance).map(Way.class::cast).collect(Collectors.toList()); - this.primitives = new HashSet<>(selection); - this.primitives.addAll(nodeReferrers); - sources = selection.stream() - .map(prim -> new Pair<>(prim, - prim.hasKey("source") ? prim.get("source") - : prim.get(GetDataRunnable.MAPWITHAI_SOURCE_TAG_KEY))) - .filter(pair -> pair.b != null).collect(Collectors.toMap(pair -> pair.a, pair -> pair.b)); - } - - @Override - public boolean executeCommand() { - GuiHelper.runInEDTAndWait(this); - return true; - } - - @Override - public void run() { - if (mapWithAI.equals(editable)) { - Logging.error("{0}: DataSet mapWithAI ({1}) should not be the same as DataSet editable ({2})", - MapWithAIPlugin.NAME, mapWithAI, editable); - throw new IllegalArgumentException(); - } - synchronized (this) { - try { - if (lock != null) { - lock.lock(); - } - if (command == null) {// needed for undo/redo (don't create a new command) - final List allPrimitives = new ArrayList<>(); - MapWithAIDataUtils.addPrimitivesToCollection(allPrimitives, primitives); - Collection primitiveData = new HashSet<>(); - final Command movePrimitivesCommand = new MovePrimitiveDataSetCommand(editable, mapWithAI, - primitives, primitiveData); - final Command createConnectionsCommand = createConnections(editable, primitiveData); - command = SequenceCommand.wrapIfNeeded(getDescriptionText(), movePrimitivesCommand, - createConnectionsCommand); - } - GuiHelper.runInEDTAndWait(command::executeCommand); - } finally { - if (lock != null) { - lock.unlock(); - } - } - } - } - - /** - * Create connections based off of current MapWithAI syntax - * - * @param dataSet The {@link DataSet} that should have the primitives we are - * connecting to - * @param collection The primitives with connection information (currently only - * checks Nodes) - * @return A Command to create connections from the collection - */ - public static Command createConnections(DataSet dataSet, Collection collection) { - return new CreateConnectionsCommand(dataSet, collection); - } - - @Override - public void undoCommand() { - try { - if (lock != null) { - lock.lock(); - } - synchronized (this) { - if (command != null) { - GuiHelper.runInEDTAndWait(command::undoCommand); - } - } - } finally { - if (lock != null) { - lock.unlock(); - } - } - } - - @Override - public String getDescriptionText() { - return tr("Add object from {0}", MapWithAIPlugin.NAME); - } - - @Override - public void fillModifiedData(Collection modified, Collection deleted, - Collection added) { - modified.addAll(primitives); - } - - @Override - public Collection getParticipatingPrimitives() { - final Command tCommand; - synchronized (this) { - tCommand = this.command; - } - return Stream - .of(Optional.ofNullable(tCommand).map(Command::getParticipatingPrimitives) - .orElseGet(Collections::emptySet), primitives) - .flatMap(Collection::stream).collect(Collectors.toSet()); - } - - /** - * Calculate the number of objects added in this command that are not deleted - * (may not count significantly modified objects as well). - * - * @return The number of MapWithAI objects added in this command that are not - * deleted - */ - public long getAddedObjects() { - long returnLong; - if (this.equals(UndoRedoHandler.getInstance().getLastCommand())) { - returnLong = primitives.size(); - } else { - returnLong = primitives.stream().map(editable::getPrimitiveById).filter(Objects::nonNull) - .filter(MapWithAIAddCommand::validPrimitive).count(); - } - return returnLong; - } - - public Collection getSourceTags() { - return sources.entrySet().stream().filter(entry -> validPrimitive(editable.getPrimitiveById(entry.getKey()))) - .map(Map.Entry::getValue).filter(Objects::nonNull).distinct().sorted().collect(Collectors.toList()); - } - - private static boolean validPrimitive(OsmPrimitive prim) { - return prim != null && (!prim.isDeleted() || prim instanceof Node); - } - - @Override - public boolean equals(Object other) { - if (other instanceof MapWithAIAddCommand) { - MapWithAIAddCommand o = (MapWithAIAddCommand) other; - return Objects.equals(this.editable, o.editable) && Objects.equals(this.mapWithAI, o.mapWithAI) - && Objects.equals(this.lock, o.lock) && o.primitives.containsAll(this.primitives) - && this.primitives.containsAll(o.primitives); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(editable, mapWithAI, primitives, lock); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeAddressBuildings.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeAddressBuildings.java deleted file mode 100644 index 10a68336..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeAddressBuildings.java +++ /dev/null @@ -1,144 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.openstreetmap.josm.command.ChangePropertyCommand; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.IPrimitive; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIPreferenceHelper; -import org.openstreetmap.josm.plugins.utilsplugin2.replacegeometry.ReplaceGeometryUtils; -import org.openstreetmap.josm.tools.Geometry; - -/** - * Merge buildings with pre-existing addresses - * - * @author Taylor Smock - * - */ -public class MergeAddressBuildings extends AbstractConflationCommand { - public static final String KEY = "building"; - public static final String SOURCE = "source"; - - public MergeAddressBuildings(DataSet data) { - super(data); - } - - @Override - public String getDescriptionText() { - return tr("Merge added buildings with existing address nodes"); - } - - @Override - public Collection> getInterestedTypes() { - return Arrays.asList(Way.class, Relation.class); - } - - @Override - public String getKey() { - return KEY; - } - - @Override - public Command getRealCommand() { - List commands = new ArrayList<>(); - if (MapWithAIPreferenceHelper.isMergeBuildingAddress()) { - final List mergedNodes = new ArrayList<>(); - possiblyAffectedPrimitives.stream().filter(Way.class::isInstance).map(Way.class::cast) - .filter(way -> way.hasKey(KEY)).filter(Way::isClosed) - .forEach(way -> commands.addAll(mergeAddressBuilding(getAffectedDataSet(), way, mergedNodes))); - - possiblyAffectedPrimitives.stream().filter(Relation.class::isInstance).map(Relation.class::cast) - .filter(rel -> rel.hasKey(KEY)).filter(Relation::isMultipolygon) - .forEach(rel -> commands.addAll(mergeAddressBuilding(getAffectedDataSet(), rel, mergedNodes))); - } - - Command returnCommand = null; - if (commands.size() == 1) { - returnCommand = commands.get(0); - } else if (!commands.isEmpty()) { - returnCommand = new SequenceCommand(getDescriptionText(), commands); - } - return returnCommand; - } - - /** - * Merge a building with an address node - * - * @param affectedDataSet The dataset to use - * @param object The object to merge with an address node - * @param mergedNodes The nodes already merged. This will be modified in - * this method! - * @return The command to merge an address onto a building - */ - private static Collection mergeAddressBuilding(DataSet affectedDataSet, OsmPrimitive object, - Collection mergedNodes) { - final List toCheck = new ArrayList<>(affectedDataSet.searchNodes(object.getBBox())); - toCheck.removeIf(IPrimitive::isDeleted); - final Collection nodesInside = Geometry.filterInsideAnyPolygon(toCheck, object); - - final List nodesWithAddresses = nodesInside.stream().filter(Node.class::isInstance).map(Node.class::cast) - .filter(node -> node.keySet().stream().anyMatch(str -> str.startsWith("addr:"))) - .filter(node -> !mergedNodes.contains(node)).collect(Collectors.toList()); - - final List commandList = new ArrayList<>(); - if (nodesWithAddresses.size() == 1 && nodesWithAddresses.stream().allMatch(n -> n.getParentWays().isEmpty())) { - String currentKey = null; - Node node = nodesWithAddresses.get(0); - mergedNodes.add(node); - List sources = new ArrayList<>(); - try { - // Remove the key to avoid the popup from utilsplugin2 - currentKey = object.get(KEY); - sources.add(object.get(SOURCE)); - object.remove(KEY); - object.remove(SOURCE); - GuiHelper.runInEDTAndWait( - () -> commandList.add(ReplaceGeometryUtils.buildUpgradeNodeCommand(node, object))); - } finally { - if (currentKey != null) { - object.put(KEY, currentKey); - } - sources.add(node.get(SOURCE)); - sources.removeIf(Objects::isNull); - sources = sources.stream().flatMap(source -> Stream.of(source.split(";", 0))).distinct() - .filter(Objects::nonNull).sorted().collect(Collectors.toList()); - if (!sources.isEmpty()) { - commandList.add(new ChangePropertyCommand(object, SOURCE, String.join(";", sources))); - } - } - } - return commandList; - } - - @Override - public boolean allowUndo() { - return false; - } - - @Override - public boolean keyShouldNotExistInOSM() { - return false; - } - - @Override - public Collection> conflictedCommands() { - return Collections.singleton(MergeBuildingAddress.class); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeBuildingAddress.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeBuildingAddress.java deleted file mode 100644 index 322a7c0d..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeBuildingAddress.java +++ /dev/null @@ -1,171 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.openstreetmap.josm.command.ChangePropertyCommand; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.DeleteCommand; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.osm.BBox; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.IPrimitive; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIPreferenceHelper; -import org.openstreetmap.josm.tools.Geometry; - -/** - * Merge addresses with pre-existing buildings - * - * @author Taylor Smock - * - */ -public class MergeBuildingAddress extends AbstractConflationCommand { - public static final String KEY = "addr:housenumber"; - - public MergeBuildingAddress(DataSet data) { - super(data); - } - - @Override - public String getDescriptionText() { - return tr("Merge added addresses with existing buildings"); - } - - @Override - public Collection> getInterestedTypes() { - return Collections.singleton(Node.class); - } - - @Override - public String getKey() { - return KEY; - } - - @Override - public Command getRealCommand() { - List commands = new ArrayList<>(); - if (MapWithAIPreferenceHelper.isMergeBuildingAddress()) { - possiblyAffectedPrimitives.stream().filter(Node.class::isInstance).map(Node.class::cast) - .filter(n -> n.hasKey(KEY)) - .forEach(n -> commands.addAll(mergeBuildingAddress(getAffectedDataSet(), n))); - } - - Command returnCommand = null; - if (commands.size() == 1) { - returnCommand = commands.get(0); - } else if (!commands.isEmpty()) { - returnCommand = new SequenceCommand(getDescriptionText(), commands); - } - return returnCommand; - } - - private Collection mergeBuildingAddress(DataSet affectedDataSet, Node node) { - final List toCheck = new ArrayList<>(); - final BBox bbox = new BBox(node.lon(), node.lat(), 0.001); - GuiHelper.runInEDTAndWait(() -> { - toCheck.addAll(affectedDataSet.searchWays(bbox)); - toCheck.addAll(affectedDataSet.searchRelations(bbox)); - toCheck.addAll(affectedDataSet.searchNodes(bbox)); - }); - List possibleDuplicates = toCheck.stream().filter(prim -> !prim.isDeleted() && prim.hasTag(KEY)) - .filter(prim -> prim.get(KEY).equals(node.get(KEY))) - .filter(prim -> !prim.equals(node) && !this.possiblyAffectedPrimitives.contains(prim)) - .collect(Collectors.toList()); - for (String tag : Arrays.asList("addr:street", "addr:unit", "addr:housenumber", "addr:housename")) { - if (node.hasTag(tag)) { - possibleDuplicates = possibleDuplicates.stream().filter(prim -> prim.hasTag(tag)) - .filter(prim -> prim.get(tag).equals(node.get(tag))).collect(Collectors.toList()); - } - } - - List buildings = toCheck.stream().filter(prim -> prim.hasTag("building")) - .filter(prim -> checkInside(node, prim)).collect(Collectors.toList()); - - final List commandList = new ArrayList<>(); - List sources = new ArrayList<>(); - OsmPrimitive object = null; - sources.add(node.get(MergeAddressBuildings.SOURCE)); - if (possibleDuplicates.size() == 1) { - final ChangePropertyCommand changePropertyCommand = new ChangePropertyCommand(possibleDuplicates, - node.getKeys()); - if (changePropertyCommand.getObjectsNumber() > 0) { - commandList.add(changePropertyCommand); - } - commandList.add(DeleteCommand.delete(Collections.singleton(node))); - object = possibleDuplicates.get(0); - } else if (buildings.size() == 1 && getAddressPoints(buildings.get(0)).size() == 1) { - commandList.add(new ChangePropertyCommand(buildings, node.getKeys())); - commandList.add(DeleteCommand.delete(Collections.singleton(node))); - object = buildings.get(0); - } - if (object != null) { - sources.add(object.get(MergeAddressBuildings.SOURCE)); - } - - sources.removeIf(Objects::isNull); - sources = sources.stream().flatMap(source -> Stream.of(source.split(";", 0))).distinct() - .filter(Objects::nonNull).sorted().collect(Collectors.toList()); - if (!sources.isEmpty() && object != null) { - commandList.add(new ChangePropertyCommand(object, MergeAddressBuildings.SOURCE, String.join(";", sources))); - } - - return commandList; - } - - private static Collection getAddressPoints(OsmPrimitive prim) { - BBox bbox = prim.getBBox(); - final Collection searchCollection = new HashSet<>(); - searchCollection.addAll(prim.getDataSet().searchNodes(bbox)); - searchCollection.addAll(prim.getDataSet().searchWays(bbox)); - searchCollection.addAll(prim.getDataSet().searchRelations(bbox)); - return Geometry.filterInsideAnyPolygon(searchCollection, prim).stream().filter(p -> !p.isDeleted()) - .filter(Node.class::isInstance).map(Node.class::cast).filter(n -> n.hasTag(KEY)) - .collect(Collectors.toList()); - } - - /** - * Check if the node is inside the other primitive - * - * @param node The node to check - * @param prim The primitive that the node may be inside - * @return true if the node is inside the primitive - */ - private static boolean checkInside(Node node, OsmPrimitive prim) { - if (prim instanceof Relation) { - return !Geometry.filterInsideMultipolygon(Collections.singleton(node), (Relation) prim).isEmpty(); - } else if (prim instanceof Way) { - return !Geometry.filterInsidePolygon(Collections.singletonList(node), (Way) prim).isEmpty(); - } - return false; - } - - @Override - public boolean allowUndo() { - return false; - } - - @Override - public boolean keyShouldNotExistInOSM() { - return false; - } - - @Override - public Collection> conflictedCommands() { - return Collections.singleton(MergeAddressBuildings.class); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeBuildingNodeCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeBuildingNodeCommand.java deleted file mode 100644 index 48a57cbb..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeBuildingNodeCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Stream; - -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Way; - -import jakarta.annotation.Nullable; - -/** - * This is similar to the ReplaceGeometryUtils.buildUpgradeNodeCommand method - * - * @author Taylor Smock - * - */ -public final class MergeBuildingNodeCommand { - private MergeBuildingNodeCommand() { - // Hide constructor - } - - /** - * Upgrade a node to a way or multipolygon - * - * @param subjectNode node to be replaced - * @param referenceObject object with greater spatial quality - * @return The command that updates the node to a way/relation - */ - @Nullable - public static Command buildUpgradeNodeCommand(Node subjectNode, OsmPrimitive referenceObject) { - boolean keepNode = !subjectNode.isNew(); - if (keepNode) { - getNewOrNoTagNode(referenceObject); - } - return null; - } - - private static Node getNewOrNoTagNode(OsmPrimitive referenceObject) { - List nodes; - if (referenceObject instanceof Way way) { - nodes = way.getNodes(); - } else if (referenceObject instanceof Relation relation) { - nodes = relation.getMemberPrimitives().stream().flatMap(o -> { - if (o instanceof Way way) { - return way.getNodes().stream(); - } else if (o instanceof Node node) { - return Stream.of(node); - } - return null; - }).filter(Objects::nonNull).toList(); - } else if (referenceObject instanceof Node node) { - nodes = Collections.singletonList(node); - } else { - throw new IllegalArgumentException(tr("Unknown OsmPrimitive type")); - } - return nodes.stream().filter(OsmPrimitive::isNew).findAny() - .orElse(nodes.stream().filter(p -> !p.isTagged()).findAny().orElse(nodes.get(0))); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeDuplicateWays.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeDuplicateWays.java deleted file mode 100644 index 019067e3..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MergeDuplicateWays.java +++ /dev/null @@ -1,412 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.openstreetmap.josm.command.ChangeCommand; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.DeleteCommand; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIPreferenceHelper; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Pair; - -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; - -/** - * Merge duplicate ways - */ -public class MergeDuplicateWays extends Command { - public static final String ORIG_ID = "orig_id"; - - private final List ways; - - private final List commands; - private Command command; - - private Bounds bound; - - /** - * Merge duplicate ways - * - * @param data Merge all duplicate ways in the dataset - */ - public MergeDuplicateWays(DataSet data) { - this(data, null, null); - } - - /** - * Merge duplicate ways - * - * @param way1 The way to merge duplicates to - */ - public MergeDuplicateWays(Way way1) { - this(way1.getDataSet(), way1, null); - } - - /** - * Merge duplicate ways - * - * @param way1 The way to merge duplicates to - * @param way2 The way to merge from - */ - public MergeDuplicateWays(Way way1, Way way2) { - this(way1.getDataSet(), way1, way2); - } - - /** - * Merge duplicate ways - * - * @param data The originating dataset - * @param way1 The first way to check against - * @param way2 The second way - */ - public MergeDuplicateWays(DataSet data, Way way1, Way way2) { - this(data, Stream.of(way1, way2).filter(Objects::nonNull).toList()); - } - - /** - * Merge duplicate ways - * - * @param data The originating dataset - * @param ways The ways to merge. If empty, the entire dataset will be checked. - */ - public MergeDuplicateWays(DataSet data, List ways) { - super(data); - this.ways = ways.stream().filter(MergeDuplicateWays::nonDeletedWay).toList(); - this.commands = new ArrayList<>(); - } - - private static boolean nonDeletedWay(Way way) { - return !way.isDeleted() && way.getNodes().stream().noneMatch(OsmPrimitive::isDeleted); - } - - @Override - public boolean executeCommand() { - if (commands.isEmpty() || (command == null)) { - if (ways.isEmpty()) { - filterDataSet(getAffectedDataSet(), commands, bound); - } else if (ways.size() == 1) { - checkForDuplicateWays(ways.get(0), commands); - } else { - final var it = ways.iterator(); - var way1 = it.next(); - while (it.hasNext()) { - final var way2 = it.next(); - final var tCommand = checkForDuplicateWays(way1, way2); - if (tCommand != null) { - commands.add(tCommand); - tCommand.executeCommand(); - } - way1 = way2; - } - } - final List realCommands = commands.stream().filter(Objects::nonNull).distinct().toList(); - commands.clear(); - commands.addAll(realCommands); - if (!commands.isEmpty() && (commands.size() != 1)) { - command = new SequenceCommand(getDescriptionText(), commands); - } else if (commands.size() == 1) { - command = commands.get(0); - } - } else { - command.executeCommand(); - } - return true; - } - - @Override - public void undoCommand() { - if (command != null) { - command.undoCommand(); - } - } - - /** - * Set the bounds for the command. Must be called before execution. - * - * @param bound The boundary for the command - */ - public void setBounds(Bounds bound) { - this.bound = bound; - } - - /** - * Look for duplicates in the dataset - * - * @param dataSet The dataset to look through - * @param commands The command list to add to - * @param bound The bounds to look at, may be {@code null} - */ - public static void filterDataSet(@Nonnull DataSet dataSet, @Nonnull List commands, - @Nullable Bounds bound) { - final List ways = (bound == null ? dataSet.getWays() : dataSet.searchWays(bound.toBBox())).stream() - .filter(prim -> !prim.isIncomplete() && !prim.isDeleted()) - .collect(Collectors.toCollection(ArrayList::new)); - for (var i = 0; i < ways.size(); i++) { - final var way1 = ways.get(i); - final var nearbyWays = dataSet.searchWays(way1.getBBox()).stream().filter(MergeDuplicateWays::nonDeletedWay) - .filter(w -> !Objects.equals(w, way1)).toList(); - for (final Way way2 : nearbyWays) { - final var command = checkForDuplicateWays(way1, way2); - final var deletedWays = new ArrayList(); - if (command != null) { - commands.add(command); - command.executeCommand(); - command.fillModifiedData(new ArrayList<>(), deletedWays, new ArrayList<>()); - if (!deletedWays.contains(way1) && !deletedWays.contains(way2)) { - commands.add(command); - } - ways.remove(way2); - } - } - } - } - - /** - * Check for ways that are (partial) duplicates, and if so merge them - * - * @param way A way to check - * @param commands A list of commands to add to - */ - public static void checkForDuplicateWays(Way way, List commands) { - final Collection nearbyWays = way.getDataSet().searchWays(way.getBBox()).stream() - .filter(MergeDuplicateWays::nonDeletedWay).filter(w -> !Objects.equals(w, way)).toList(); - for (final Way way2 : nearbyWays) { - if (!way2.isDeleted()) { - final var tCommand = checkForDuplicateWays(way, way2); - if (tCommand != null) { - commands.add(tCommand); - tCommand.executeCommand(); - } - } - } - } - - /** - * Check if ways are (partial) duplicates, and if so create a command to merge - * them - * - * @param way1 A way to check - * @param way2 A way to check - * @return non-null command if they are duplicate ways - */ - public static Command checkForDuplicateWays(Way way1, Way way2) { - Command returnCommand = null; - final Map, Map> duplicateNodes = getDuplicateNodes(way1, way2); - final Set, Map>> duplicateEntrySet = duplicateNodes.entrySet(); - final Set, Pair>> compressed = duplicateNodes.entrySet().stream() - .map(entry -> new Pair<>(entry.getKey(), - new Pair<>(entry.getValue().entrySet().iterator().next().getKey(), - entry.getValue().entrySet().iterator().next().getValue()))) - .sorted(Comparator.comparingInt(pair -> pair.a.a)).collect(Collectors.toCollection(LinkedHashSet::new)); - if (compressed.stream().anyMatch(entry -> entry.a.b.isDeleted() || entry.b.b.isDeleted())) { - Logging.error("Bad node"); - Logging.error("{0}", way1); - Logging.error("{0}", way2); - } - if ((compressed.size() > 1) && duplicateEntrySet.stream().noneMatch(entry -> entry.getValue().size() > 1)) { - final var initial = compressed.stream().map(entry -> entry.a.a).sorted().toList(); - final var after = compressed.stream().map(entry -> entry.b.a).sorted().toList(); - if (sorted(initial) && sorted(after)) { - returnCommand = mergeWays(way1, way2, compressed); - } - } else if (compressed.isEmpty() && way1.hasKey(ORIG_ID) && way1.get(ORIG_ID).equals(way2.get(ORIG_ID))) { - returnCommand = mergeWays(way1, way2, compressed); - } - return returnCommand; - } - - /** - * Merge ways with multiple common nodes - * - * @param way1 The way to keep - * @param way2 The way to remove while moving its nodes to way1 - * @param compressed The duplicate nodes - * @return A command to merge ways, null if not possible - */ - public static Command mergeWays(Way way1, Way way2, - Set, Pair>> compressed) { - Command command = null; - if ((compressed.size() > 1) || (way1.hasKey(ORIG_ID) && way1.get(ORIG_ID).equals(way2.get(ORIG_ID)))) { - Set, Pair>> realSet = new LinkedHashSet<>(compressed); - final boolean sameDirection = checkDirection(realSet); - final List way2Nodes = way2.getNodes(); - if (!sameDirection) { - Collections.reverse(way2Nodes); - realSet = realSet.stream().map(pair -> { - pair.b.a = way2Nodes.size() - pair.b.a - 1; - return pair; - }).collect(Collectors.toSet()); - } - final int last = realSet.stream().mapToInt(pair -> pair.b.a).max().orElse(way2Nodes.size()); - final int first = realSet.stream().mapToInt(pair -> pair.b.a).min().orElse(0); - final List before = new ArrayList<>(); - final List after = new ArrayList<>(); - for (final Node node : way2Nodes) { - final int position = way2Nodes.indexOf(node); - if (position < first) { - before.add(node); - } else if (position > last) { - after.add(node); - } - } - Collections.reverse(before); - final var newWay = new Way(way1); - List commands = new ArrayList<>(); - before.forEach(node -> newWay.addNode(0, node)); - after.forEach(newWay::addNode); - if (newWay.getNodesCount() > 0) { - final var changeCommand = new ChangeCommand(way1, newWay); - commands.add(changeCommand); - /* - * This must be executed, otherwise the delete command will believe that way2 - * nodes don't belong to anything See Issue #46 - */ - changeCommand.executeCommand(); - commands.add(DeleteCommand.delete(Collections.singleton(way2), true, true)); - /* - * Just to ensure that the dataset is consistent prior to the "real" - * executeCommand - */ - changeCommand.undoCommand(); - } - if (commands.contains(null)) { - commands = commands.stream().filter(Objects::nonNull).collect(Collectors.toList()); - } - if (!commands.isEmpty()) { - command = new SequenceCommand(tr("Merge ways"), commands); - } - } - return command; - } - - /** - * Find a node's duplicate in a set of duplicates - * - * @param node The node to find in the set - * @param compressed The set of node duplicates - * @return The node that the param {@code node} duplicates - */ - public static Node nodeInCompressed(Node node, Set, Pair>> compressed) { - var returnNode = node; - for (final var pair : compressed) { - if (node.equals(pair.a.b)) { - returnNode = pair.b.b; - } else if (node.equals(pair.b.b)) { - returnNode = pair.a.b; - } - if (!node.equals(returnNode)) { - break; - } - } - final var tReturnNode = returnNode; - node.getKeys().forEach(tReturnNode::put); - return returnNode; - } - - /** - * Check if the node pairs increment in the same direction (only checks first - * two pairs), ensure that they are sorted with {@link #sorted} - * - * @param compressed The set of duplicate node/placement pairs - * @return true if the node pairs increment in the same direction - */ - public static boolean checkDirection(Set, Pair>> compressed) { - final var iterator = compressed.iterator(); - var returnValue = false; - if (compressed.size() > 1) { - final var first = iterator.next(); - final var second = iterator.next(); - final var way1Forward = first.a.a < second.a.a; - final var way2Forward = first.b.a < second.b.a; - returnValue = way1Forward == way2Forward; - } - return returnValue; - } - - /** - * Check if a list is a consecutively increasing number list - * - * @param collection The list of integers - * @return true if there are no gaps and it increases - */ - public static boolean sorted(List collection) { - var returnValue = true; - if (collection.size() > 1) { - Integer last = collection.get(0); - for (var i = 1; i < collection.size(); i++) { - final Integer next = collection.get(i); - if ((next - last) != 1) { - returnValue = false; - break; - } - last = next; - } - } - return returnValue; - } - - /** - * Get duplicate nodes from two ways - * - * @param way1 An initial way with nodes - * @param way2 A way that may have duplicate nodes with way1 - * @return A map of node -> node(s) duplicates - */ - public static Map, Map> getDuplicateNodes(Way way1, Way way2) { - final var duplicateNodes = new LinkedHashMap, Map>(); - for (var j = 0; j < way1.getNodesCount(); j++) { - final var origNode = way1.getNode(j); - for (var k = 0; k < way2.getNodesCount(); k++) { - final var possDupeNode = way2.getNode(k); - if (origNode.equals(possDupeNode) || (origNode - .greatCircleDistance(possDupeNode) < MapWithAIPreferenceHelper.getMaxNodeDistance())) { - final var origNodePair = new Pair<>(j, origNode); - final var dupeNodeMap = duplicateNodes.computeIfAbsent(origNodePair, ignored -> new HashMap<>()); - dupeNodeMap.put(k, possDupeNode); - } - } - } - return duplicateNodes; - } - - @Override - public String getDescriptionText() { - return tr("Merge ways"); - } - - @Override - public void fillModifiedData(Collection modified, Collection deleted, - Collection added) { - for (final Command currentCommand : commands) { - currentCommand.fillModifiedData(modified, deleted, added); - } - } - - @Override - public Collection getParticipatingPrimitives() { - return commands.stream().flatMap(currentCommand -> currentCommand.getParticipatingPrimitives().stream()) - .collect(Collectors.toSet()); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MovePrimitiveDataSetCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MovePrimitiveDataSetCommand.java deleted file mode 100644 index 6fa6d895..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/MovePrimitiveDataSetCommand.java +++ /dev/null @@ -1,199 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands; - -import static org.openstreetmap.josm.tools.I18n.tr; -import static org.openstreetmap.josm.tools.I18n.trn; - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.command.AddPrimitivesCommand; -import org.openstreetmap.josm.command.ChangePropertyCommand; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.DeleteCommand; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.PrimitiveData; -import org.openstreetmap.josm.data.osm.visitor.MergeSourceBuildingVisitor; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.backend.GetDataRunnable; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.bugreport.ReportedException; - -/** - * Move primitives between datasets (*not* a copy) - * - * @author Taylor Smock - */ -public class MovePrimitiveDataSetCommand extends Command { - private Command command; - - /** - * Move primitives from one dataset to another - * - * @param to The destination dataset - * @param from The originating dataset - * @param primitives The primitives to move - */ - public MovePrimitiveDataSetCommand(DataSet to, DataSet from, Collection primitives) { - super(to); - if (from == null || to.isLocked() || from.isLocked() || to.equals(from)) { - Logging.error("{0}: Cannot move primitives from {1} to {2}", MapWithAIPlugin.NAME, from, to); - } else { - command = moveCollection(from, to, primitives); - } - } - - /** - * Move primitives from one dataset to another - * - * @param to The destination dataset - * @param from The originating dataset - * @param primitives The primitives to move - * @param primitiveData A collection to be add the primitive data to (important - * if any positive ids are available) - */ - public MovePrimitiveDataSetCommand(DataSet to, DataSet from, Collection primitives, - Collection primitiveData) { - super(to); - if (from == null || to.isLocked() || from.isLocked() || to.equals(from)) { - Logging.error("{0}: Cannot move primitives from {1} to {2}", MapWithAIPlugin.NAME, from, to); - } else { - command = moveCollection(from, to, primitives, primitiveData); - } - } - - @Override - public boolean executeCommand() { - if (command != null) { - // DeleteCommand is used when the MapWithAI layer has been deleted. - if (command instanceof DeleteCommand) { - command.undoCommand(); - } else { - command.getAffectedDataSet().update(command::executeCommand); - } - } - return true; - } - - /** - * Move primitives from one dataset to another - * - * @param to The receiving dataset - * @param from The sending dataset - * @param selection The primitives to move - * @return The command that does the actual move - */ - public static Command moveCollection(DataSet from, DataSet to, Collection selection) { - return moveCollection(from, to, selection, new HashSet<>()); - } - - /** - * Move primitives from one dataset to another - * - * @param to The receiving dataset - * @param from The sending dataset - * @param selection The primitives to move - * @param primitiveData A collection to be add the primitive data to (important - * if any positive ids are available) - * @return The command that does the actual move - */ - public static Command moveCollection(DataSet from, DataSet to, Collection selection, - Collection primitiveData) { - final var commands = new ArrayList(); - - final var selected = from.getAllSelected(); - GuiHelper.runInEDTAndWait(() -> from.setSelected(selection)); - final var builder = new MergeSourceBuildingVisitor(from); - final var hull = builder.build(); - GuiHelper.runInEDTAndWait(() -> from.setSelected(selected)); - - final var primitiveAddData = hull.allPrimitives().stream().map(OsmPrimitive::save).toList(); - primitiveAddData.stream().map(data -> { - if (data.getUniqueId() > 0) { - // Don't do this with conn data? - data.clearOsmMetadata(); - } - return data; - }).forEach(data -> data.remove(GetDataRunnable.MAPWITHAI_SOURCE_TAG_KEY)); - primitiveData.addAll(primitiveAddData); - - commands.add(new AddPrimitivesCommand(primitiveAddData, - selection.stream().map(OsmPrimitive::save).collect(Collectors.toList()), to)); - final var removeKeyCommand = new ArrayList(); - final var fullSelection = new HashSet(); - MapWithAIDataUtils.addPrimitivesToCollection(fullSelection, selection); - if (!fullSelection.isEmpty()) { - CreateConnectionsCommand.getConflationCommands().forEach(clazz -> { - try { - removeKeyCommand.add(new ChangePropertyCommand(fullSelection, - clazz.getConstructor(DataSet.class).newInstance(from).getKey(), null)); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | NoSuchMethodException | SecurityException e) { - Logging.error(e); - } - }); - } - Command delete; - if (!removeKeyCommand.isEmpty()) { - final var sequence = new SequenceCommand("Temporary Command", removeKeyCommand); - sequence.executeCommand(); // This *must* be executed for the delete command to get everything. - delete = DeleteCommand.delete(selection, true, true); - sequence.undoCommand(); - } else { - delete = DeleteCommand.delete(selection, true, true); - } - commands.add(delete); - commands.removeIf(Objects::isNull); - - if (!commands.isEmpty()) { - return SequenceCommand.wrapIfNeeded(trn("Move {0} OSM Primitive between data sets", - "Move {0} OSM Primitives between data sets", selection.size(), selection.size()), commands); - } - return null; - } - - @Override - public void undoCommand() { - if (command != null) { - try { - if (command instanceof DeleteCommand) { - command.executeCommand(); - } else { - command.undoCommand(); - } - } catch (ReportedException | AssertionError e) { - if (!e.getMessage().contains("Primitive is of wrong data set for this command")) { - throw e; - } - command = DeleteCommand.delete(command.getParticipatingPrimitives()); - command.executeCommand(); - } - } - } - - @Override - public String getDescriptionText() { - return tr("Move OsmPrimitives between layers"); - } - - @Override - public void fillModifiedData(Collection modified, Collection deleted, - Collection added) { - command.fillModifiedData(modified, deleted, added); - } - - @Override - public Collection getParticipatingPrimitives() { - return Optional.ofNullable(command).map(Command::getParticipatingPrimitives).orElseGet(Collections::emptySet); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/cleanup/MissingConnectionTags.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/cleanup/MissingConnectionTags.java deleted file mode 100644 index 3532815f..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/cleanup/MissingConnectionTags.java +++ /dev/null @@ -1,329 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands.cleanup; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.JOptionPane; - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.actions.AutoScaleAction; -import org.openstreetmap.josm.command.ChangePropertyCommand; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.IPrimitive; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper; -import org.openstreetmap.josm.data.validation.TestError; -import org.openstreetmap.josm.data.validation.tests.CrossingWays; -import org.openstreetmap.josm.data.validation.tests.DuplicateNode; -import org.openstreetmap.josm.data.validation.tests.UnconnectedWays; -import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.layer.AbstractOsmDataLayer; -import org.openstreetmap.josm.gui.progress.NullProgressMonitor; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.plugins.mapwithai.commands.AbstractConflationCommand; -import org.openstreetmap.josm.plugins.mapwithai.commands.CreateConnectionsCommand; -import org.openstreetmap.josm.spi.preferences.Config; -import org.openstreetmap.josm.tools.Geometry; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Utils; - -/** - * This checks for missing dupe tags, and asks the user if it is a duplicate - * - * @author Taylor Smock - */ -public class MissingConnectionTags extends AbstractConflationCommand { - private double precision; - private static final String HIGHWAY = "highway"; - - public MissingConnectionTags(DataSet data) { - super(data); - } - - @Override - public String getDescriptionText() { - return tr("Deduplicate nodes and connect ways"); - } - - @Override - public Collection> getInterestedTypes() { - return Collections.singleton(Way.class); - } - - @Override - public String getKey() { - // For now, we assume that only highways are at issue. - return HIGHWAY; - } - - @Override - public Command getRealCommand() { - precision = Config.getPref().getDouble("validator.duplicatenodes.precision", 0.); - // precision is in meters - precision = precision == 0 ? 1 : precision; - final var current = MainApplication.getLayerManager().getActiveLayer(); - final var ways = Utils.filteredCollection(possiblyAffectedPrimitives, Way.class); - if (!ways.isEmpty()) { - final var ds = this.getAffectedDataSet(); - MainApplication.getLayerManager().getLayersOfType(AbstractOsmDataLayer.class).stream() - .filter(d -> ds.equals(d.getDataSet())).findAny() - .ifPresent(toSwitch -> MainApplication.getLayerManager().setActiveLayer(toSwitch)); - } - final var prefKey = "mapwithai.conflation.missingconflationtags"; - - final var selection = getAffectedDataSet().getAllSelected(); - ConditionalOptionPaneUtil.startBulkOperation(prefKey); - final var commands = new ArrayList(); - fixErrors(prefKey, commands, findDuplicateNodes(possiblyAffectedPrimitives)); - fixErrors(prefKey, commands, findCrossingWaysAtNodes(possiblyAffectedPrimitives)); - fixErrors(prefKey, commands, findUnconnectedWays(possiblyAffectedPrimitives)); - ConditionalOptionPaneUtil.endBulkOperation(prefKey); - if (current != null) { - MainApplication.getLayerManager().setActiveLayer(current); - } - GuiHelper.runInEDT(() -> getAffectedDataSet().setSelected(selection)); - if (commands.size() == 1) { - return commands.iterator().next(); - } else if (!commands.isEmpty()) { - return new SequenceCommand(tr("Perform missing conflation steps"), commands); - } - return null; - } - - protected void fixErrors(String prefKey, Collection commands, Collection issues) { - for (var issue : issues) { - if (!issue.isFixable() || issue.getFix() == null - || issue.getPrimitives().stream().anyMatch(IPrimitive::isDeleted)) { - continue; - } - GuiHelper.runInEDT(() -> getAffectedDataSet().setSelected(issue.getPrimitives())); - final var primitives = issue.getPrimitives(); - if (primitives.stream().noneMatch(Node.class::isInstance)) { - AutoScaleAction.zoomTo(issue.getPrimitives()); - } else { - AutoScaleAction.zoomTo(issue.getPrimitives().stream().filter(Node.class::isInstance) - .map(Node.class::cast).collect(Collectors.toList())); - } - final var message = issue.getFix().getDescriptionText(); - boolean fixIt = ConditionalOptionPaneUtil.showConfirmationDialog(prefKey, MainApplication.getMainFrame(), - message, tr("Possible missing conflation key"), JOptionPane.YES_NO_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_OPTION); - if (fixIt) { - final var command = issue.getFix(); - command.executeCommand(); - commands.add(command); - } - } - } - - /** - * Find nodes that may be missing a dupe tag - * - * @param possiblyAffectedPrimitives The primitives that may be affected - * @return The issues - */ - protected static Collection findDuplicateNodes(Collection possiblyAffectedPrimitives) { - final var issues = new ArrayList(); - final var duplicateNodeTest = new DuplicateNode(); - for (var way : Utils.filteredCollection(possiblyAffectedPrimitives, Way.class)) { - for (var node : way.getNodes()) { - duplicateNodeTest.startTest(NullProgressMonitor.INSTANCE); - final var searchBBox = node.getBBox(); - searchBBox.addPrimitive(node, 0.001); - way.getDataSet().searchNodes(searchBBox).stream().filter(MissingConnectionTags::noConflationKey) - .forEach(duplicateNodeTest::visit); - duplicateNodeTest.endTest(); - final var dupeNodes = duplicateNodeTest.getErrors().stream() - .filter(e -> e.getPrimitives().contains(node)).flatMap(e -> e.getPrimitives().stream()) - .distinct() - .filter(p -> !p.isDeleted() && !p.equals(node) && noConflationKey(p) && p.getOsmId() > 0) - .toList(); - if (duplicateNodeTest.getErrors().isEmpty() || dupeNodes.isEmpty()) { - continue; - } - final var dupes = duplicateNodeTest.getErrors().stream().filter(e -> e.getPrimitives().contains(node)) - .flatMap(e -> e.getPrimitives().stream()).distinct() - .filter(p -> !p.isDeleted() && !p.equals(node)).map(OsmPrimitive::getPrimitiveId) - .map(Object::toString).toList(); - - final var initial = duplicateNodeTest.getErrors().get(0); - final var prims = new ArrayList(dupeNodes); - prims.add(node); - issues.add(TestError.builder(initial.getTester(), initial.getSeverity(), initial.getCode()) - .message(initial.getMessage()).primitives(prims) - .fix(() -> new ChangePropertyCommand(node, "dupe", String.join(",", dupes))).build()); - duplicateNodeTest.clear(); - } - } - return issues; - } - - /** - * Find nodes that may be missing a conn tag - * - * @param possiblyAffectedPrimitives The primitives that may be affected - * @return The issues found - */ - protected Collection findCrossingWaysAtNodes(Collection possiblyAffectedPrimitives) { - final var issues = new ArrayList(); - final var crossingWays = new CrossingWays.Ways(); - for (var way : Utils.filteredCollection(possiblyAffectedPrimitives, Way.class)) { - final var seenFix = new HashSet(); - crossingWays.startTest(NullProgressMonitor.INSTANCE); - way.getDataSet().searchWays(way.getBBox()).stream().filter(w -> w.hasKey(HIGHWAY)) - .forEach(crossingWays::visit); - crossingWays.endTest(); - for (var error : crossingWays.getErrors()) { - if (seenFix.containsAll(error.getPrimitives()) || error.getPrimitives().stream() - .filter(Way.class::isInstance).map(Way.class::cast).noneMatch(w -> w.hasKey(HIGHWAY))) { - continue; - } - final var fixError = TestError.builder(error.getTester(), error.getSeverity(), error.getCode()) - .primitives(error.getPrimitives()).fix(createIntersectionCommandSupplier(error, way, precision)) - .message(error.getMessage()); - seenFix.addAll(error.getPrimitives()); - issues.add(fixError.build()); - } - crossingWays.clear(); - } - return issues; - } - - private static Supplier createIntersectionCommandSupplier(TestError error, Way way, double precision) { - final var nodes = Geometry.addIntersections(error.getPrimitives().stream().filter(Way.class::isInstance) - .map(Way.class::cast).filter(w -> w.hasKey(HIGHWAY)).collect(Collectors.toList()), false, - new ArrayList<>()); - if (nodes.stream().filter(MissingConnectionTags::noConflationKey).filter(Objects::nonNull) - .anyMatch(n -> way.getNodes().stream().filter(MissingConnectionTags::noConflationKey) - .filter(Objects::nonNull).anyMatch(wn -> n.greatCircleDistance(wn) < precision))) { - return () -> createIntersectionCommand(way, - way.getNodes().stream().filter(MissingConnectionTags::noConflationKey) - .filter(n1 -> nodes.stream().anyMatch(n2 -> Geometry.getDistance(n1, n2) < precision)) - .collect(Collectors.toList()), - precision); - } - return null; - } - - private static Command createIntersectionCommand(Way way, Collection intersectionNodes, double precision) { - final var commands = new ArrayList(); - if (intersectionNodes.stream().anyMatch(way::containsNode)) { - final var searchWays = way.getDataSet().searchWays(way.getBBox()).stream() - .filter(w -> w != way && w.hasKey(HIGHWAY)).toList(); - for (var potential : searchWays) { - for (var node : way.getNodes()) { - final var command = createAddNodeCommand(potential, node, precision); - if (command != null) { - commands.add(command); - } - } - } - } - if (commands.size() == 1) { - return commands.iterator().next(); - } else if (!commands.isEmpty()) { - return new SequenceCommand(tr("Create intersections"), commands); - } - return null; - } - - private static Command createAddNodeCommand(Way way, Node node, double precision) { - if (Geometry.getDistance(node, way) < precision) { - final var seg = Geometry.getClosestWaySegment(way, node); - final var prims = Arrays.asList(way, seg.getFirstNode(), seg.getSecondNode()); - if (prims.stream().allMatch(p -> p.getOsmId() > 0)) { - return new ChangePropertyCommand(node, "conn", - prims.stream().map(p -> p.getPrimitiveId().toString()).collect(Collectors.joining(","))); - } - } - return null; - } - - protected static Collection findUnconnectedWays(Collection possiblyAffectedPrimitives) { - final var unconnectedWays = new UnconnectedWays.UnconnectedHighways(); - unconnectedWays.startTest(NullProgressMonitor.INSTANCE); - unconnectedWays.visit(possiblyAffectedPrimitives); - unconnectedWays.endTest(); - double p = Config.getPref().getDouble( - ValidatorPrefHelper.PREFIX + "." + UnconnectedWays.class.getSimpleName() + ".node_way_distance", 10.0); - // precision is in meters - double precision = p == 0 ? 1 : p; - - final var primsAndChildren = possiblyAffectedPrimitives.stream().filter(Way.class::isInstance) - .map(Way.class::cast).map(Way::getNodes).flatMap(List::stream) - .filter(MissingConnectionTags::noConflationKey).collect(Collectors.toSet()); - final var issues = new ArrayList(); - for (var issue : unconnectedWays.getErrors()) { - if (issue.isFixable() || issue.getPrimitives().stream().noneMatch(primsAndChildren::contains)) { - if (issue.isFixable() && issue.getPrimitives().stream().filter(MissingConnectionTags::noConflationKey) - .anyMatch(primsAndChildren::contains)) { - issues.add(issue); - } - continue; - } - final var way = Utils.filteredCollection(new ArrayList<>(issue.getPrimitives()), Way.class).stream() - .findAny().orElse(null); - final var node = Utils.filteredCollection(new ArrayList<>(issue.getPrimitives()), Node.class).stream() - .findAny().orElse(null); - if (way != null && node != null) { - final var issueBuilder = TestError.builder(issue.getTester(), issue.getSeverity(), issue.getCode()) - .message(issue.getMessage()).primitives(issue.getPrimitives()); - issueBuilder.fix(() -> createAddNodeCommand(way, node, precision)); - issues.add(issueBuilder.build()); - } - } - return issues; - } - - private static boolean noConflationKey(OsmPrimitive prim) { - return CreateConnectionsCommand.getConflationCommands().stream().map(c -> { - try { - return c.getConstructor(DataSet.class) - .newInstance(prim.getDataSet() == null ? new DataSet() : prim.getDataSet()); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | NoSuchMethodException | SecurityException e) { - Logging.error(e); - } - return null; - }).filter(Objects::nonNull).noneMatch(c -> prim.hasKey(c.getKey())); - } - - @Override - public boolean allowUndo() { - return false; - } - - @Override - public boolean keyShouldNotExistInOSM() { - return false; - } - - @Override - public boolean equals(Object other) { - if (other instanceof MissingConnectionTags missingConnectionTags) { - return missingConnectionTags.precision == this.precision && super.equals(missingConnectionTags); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), precision); - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/cleanup/OverNodedWays.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/cleanup/OverNodedWays.java deleted file mode 100644 index 3fd8274e..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/cleanup/OverNodedWays.java +++ /dev/null @@ -1,144 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.commands.cleanup; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; - -import org.openstreetmap.josm.actions.AutoScaleAction; -import org.openstreetmap.josm.actions.SimplifyWayAction; -import org.openstreetmap.josm.command.Command; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.layer.AbstractOsmDataLayer; -import org.openstreetmap.josm.gui.layer.Layer; -import org.openstreetmap.josm.plugins.mapwithai.commands.AbstractConflationCommand; -import org.openstreetmap.josm.spi.preferences.Config; -import org.openstreetmap.josm.tools.Utils; - -/** - * Look for ways that have too many nodes - */ -public class OverNodedWays extends AbstractConflationCommand { - - private class PostponedOverNodedWayCommand extends Command { - private Command realCommand; - - protected PostponedOverNodedWayCommand(DataSet data) { - super(data); - } - - @Override - public boolean executeCommand() { - if (realCommand == null) { - double threshold = Config.getPref().getDouble("mapwithai.conflation.simplifyway", 0.5); - int acceptableRemovalPercentage = Config.getPref() - .getInt("mapwithai.conflation.simplifywaynodepercentagerequired", 20); - Layer current = MainApplication.getLayerManager().getActiveLayer(); - Collection ways = Utils.filteredCollection(possiblyAffectedPrimitives, Way.class); - if (!ways.isEmpty()) { - DataSet ds = this.getAffectedDataSet(); - Layer toSwitch = MainApplication.getLayerManager().getLayersOfType(AbstractOsmDataLayer.class) - .stream().filter(d -> ds.equals(d.getDataSet())).findAny().orElse(null); - if (toSwitch != null) { - MainApplication.getLayerManager().setActiveLayer(toSwitch); - } - } - Collection commands = new ArrayList<>(); - for (Way way : ways) { - Command command = SimplifyWayAction.createSimplifyCommand(way, threshold); - if (command == null) { - continue; - } - int count = Utils - .filteredCollection(new ArrayList<>(command.getParticipatingPrimitives()), Node.class) - .size(); - if ((count / (double) way.getNodesCount()) * 100 > acceptableRemovalPercentage) { - AutoScaleAction.zoomTo(Collections.singleton(way)); - double length = SimplifyWayAction.askSimplifyWays( - tr("You are about to simplify {0} way with a total length of {1}.", 1, way.getLength()), - true); - command = SimplifyWayAction.createSimplifyCommand(way, length); - if (command != null) { - commands.add(command); - } - } - } - if (current != null) { - MainApplication.getLayerManager().setActiveLayer(current); - } - if (!commands.isEmpty()) { - realCommand = SequenceCommand.wrapIfNeeded(tr("Simplify ways"), commands); - realCommand.executeCommand(); - } - - } else { - realCommand.executeCommand(); - } - return true; - } - - @Override - public void undoCommand() { - if (realCommand != null) { - realCommand.undoCommand(); - } - } - - @Override - public String getDescriptionText() { - return realCommand != null ? realCommand.getDescriptionText() : "Command not run"; - } - - @Override - public void fillModifiedData(Collection modified, Collection deleted, - Collection added) { - if (realCommand != null) { - realCommand.fillModifiedData(modified, deleted, added); - } - } - - } - - public OverNodedWays(DataSet data) { - super(data); - } - - @Override - public String getDescriptionText() { - return tr("Fix overnoded ways"); - } - - @Override - public Collection> getInterestedTypes() { - return Collections.singleton(Way.class); - } - - @Override - public String getKey() { - // Currently only interested in highways - return "highway"; - } - - @Override - public Command getRealCommand() { - return new PostponedOverNodedWayCommand(this.getAffectedDataSet()); - } - - @Override - public boolean allowUndo() { - return false; - } - - @Override - public boolean keyShouldNotExistInOSM() { - return false; - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/CountryUtils.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/CountryUtils.java deleted file mode 100644 index 680316d8..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/CountryUtils.java +++ /dev/null @@ -1,116 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.awt.geom.AffineTransform; -import java.awt.geom.PathIterator; -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import org.apache.commons.jcs3.access.CacheAccess; -import org.openstreetmap.josm.data.cache.JCSCacheManager; -import org.openstreetmap.josm.data.coor.LatLon; -import org.openstreetmap.josm.data.imagery.ImageryInfo; -import org.openstreetmap.josm.data.imagery.Shape; -import org.openstreetmap.josm.data.osm.BBox; -import org.openstreetmap.josm.data.sources.SourceBounds; -import org.openstreetmap.josm.tools.DefaultGeoProperty; -import org.openstreetmap.josm.tools.GeoPropertyIndex; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Territories; - -/** - * Get country data for use in info classes - */ -public final class CountryUtils { - private static final CacheAccess CACHED_COUNTRIES = JCSCacheManager - .getCache("mapwithai:country:shapes"); - - private CountryUtils() { - /* Hide constructor */ - } - - /** - * Get the country shape - * - * @param country The country to get the shape for - * @return The (optional) bounds (may be empty if no country matched) - */ - public static Optional getCountryShape(String country) { - ImageryInfo.ImageryBounds shape = CACHED_COUNTRIES.get(country); - if (shape != null) { - return Optional.of(shape); - } - GeoPropertyIndex geoPropertyIndex = Territories.getGeoPropertyIndex(country); - if (geoPropertyIndex != null && geoPropertyIndex.getGeoProperty() instanceof DefaultGeoProperty) { - DefaultGeoProperty prop = (DefaultGeoProperty) geoPropertyIndex.getGeoProperty(); - Rectangle2D areaBounds = prop.getArea().getBounds2D(); - ImageryInfo.ImageryBounds tmp = new ImageryInfo.ImageryBounds(bboxToBoundsString( - new BBox(areaBounds.getMinX(), areaBounds.getMinY(), areaBounds.getMaxX(), areaBounds.getMaxY())), - ","); - areaToShapes(prop.getArea()).forEach(tmp::addShape); - CACHED_COUNTRIES.put(country, tmp); - return Optional.of(tmp); - } - return Optional.empty(); - } - - /** - * Get the country for a given shape - * - * @param shape The shape to get a country for - * @return The country, if found - */ - public static Optional shapeToCountry(Shape shape) { - for (String country : Territories.getKnownIso3166Codes()) { - List shapes = getCountryShape(country).map(SourceBounds::getShapes) - .orElseGet(Collections::emptyList); - for (Shape checkShape : shapes) { - if (Objects.equals(shape, checkShape)) { - return Optional.of(country); - } - } - } - return Optional.empty(); - } - - private static Collection areaToShapes(java.awt.Shape shape) { - PathIterator iterator = shape.getPathIterator(new AffineTransform()); - Shape defaultShape = new Shape(); - Collection shapes = new ArrayList<>(); - float[] moveTo = null; - float[] coords = new float[6]; - while (!iterator.isDone()) { - int type = iterator.currentSegment(coords); - if (type == PathIterator.SEG_MOVETO || type == PathIterator.SEG_LINETO) { - if (type == PathIterator.SEG_MOVETO) { - moveTo = coords; - } - defaultShape.addPoint(Float.toString(coords[1]), Float.toString(coords[0])); - } else if (type == PathIterator.SEG_CLOSE && moveTo != null) { - defaultShape.addPoint(Float.toString(moveTo[1]), Float.toString(moveTo[0])); - shapes.add(defaultShape); - defaultShape = new Shape(); - } else { - Logging.error(tr("No implementation for converting a segment of type {0} to coordinates", type)); - } - iterator.next(); - } - if (!defaultShape.getPoints().isEmpty()) { - shapes.add(defaultShape); - } - return shapes; - } - - private static String bboxToBoundsString(BBox bbox) { - return String.join(",", LatLon.cDdFormatter.format(bbox.getBottomRightLat()), - LatLon.cDdFormatter.format(bbox.getTopLeftLon()), LatLon.cDdFormatter.format(bbox.getTopLeftLat()), - LatLon.cDdFormatter.format(bbox.getBottomRightLon())); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAICategory.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAICategory.java deleted file mode 100644 index d051b8a9..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAICategory.java +++ /dev/null @@ -1,121 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.marktr; - -import java.io.Serial; -import java.io.Serializable; -import java.util.Collections; -import java.util.Comparator; -import java.util.EnumMap; -import java.util.Locale; -import java.util.Map; - -import javax.swing.ImageIcon; - -import org.openstreetmap.josm.data.sources.ISourceCategory; -import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets; -import org.openstreetmap.josm.tools.ImageProvider; -import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; - -import jakarta.annotation.Nonnull; - -/** - * The category for a MapWithAI source (i.e., buildings/highways/addresses/etc.) - * - * @author Taylor Smock - * - */ -public enum MapWithAICategory implements ISourceCategory { - - BUILDING("data/closedway", "buildings", marktr("Buildings")), HIGHWAY("presets/transport/way/way_road", "highways", - marktr("Roads")), ADDRESS("presets/misc/housenumber_small", "addresses", - marktr("Addresses")), POWER("presets/power/pole", "pole", marktr("Power")), PRESET("dialogs/search", - "presets", marktr("Presets")), FEATURED("presets/service/network-wireless", "featured", - marktr("Featured")), PREVIEW("presets/misc/fixme", "preview", - marktr("Preview")), OTHER(null, "other", marktr("Other")); - - private static final Map> iconCache = Collections - .synchronizedMap(new EnumMap<>(ImageSizes.class)); - - private final String category; - private final String description; - private final String icon; - - MapWithAICategory(String icon, String category, String description) { - this.category = category; - this.icon = icon == null || icon.trim().isEmpty() ? "mapwithai" : icon; - this.description = description; - } - - @Override - public final String getCategoryString() { - return category; - } - - @Override - public final String getDescription() { - return description; - } - - @Override - public final ImageIcon getIcon(ImageSizes size) { - return iconCache.computeIfAbsent(size, x -> Collections.synchronizedMap(new EnumMap<>(MapWithAICategory.class))) - .computeIfAbsent(this, x -> ImageProvider.get(x.icon, size)); - } - - /** - * Get the category from a string - * - * @param s The string to get the category from - * @return The category, if found, else {@link #OTHER} - */ - @Nonnull - public static MapWithAICategory fromString(String s) { - for (MapWithAICategory category : MapWithAICategory.values()) { - if (category.getCategoryString().equals(s)) { - return category; - } - } - if (s != null && !s.trim().isEmpty()) { - // fuzzy match - String tmp = s.toLowerCase(Locale.ROOT); - for (MapWithAICategory type : MapWithAICategory.values()) { - if (tmp.contains(type.getDescription().toLowerCase(Locale.ROOT)) - || type.getDescription().toLowerCase(Locale.ROOT).contains(tmp)) { - return type; - } - } - // Check if it matches a preset - if (TaggingPresets.getPresetKeys().stream().map(String::toLowerCase) - .anyMatch(m -> tmp.contains(m) || m.contains(tmp))) { - return PRESET; - } - } - return OTHER; - } - - /** - * Compare two MapWithAI categories - */ - public static class DescriptionComparator implements Comparator, Serializable { - - @Serial - private static final long serialVersionUID = 9131636715279880580L; - - @Override - public int compare(MapWithAICategory o1, MapWithAICategory o2) { - return (o1 == null || o2 == null) ? 1 : o1.getDescription().compareTo(o2.getDescription()); - } - } - - @Override - public MapWithAICategory getDefault() { - return OTHER; - } - - @Override - public MapWithAICategory getFromString(String s) { - return fromString(s); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIConflationCategory.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIConflationCategory.java deleted file mode 100644 index be101b08..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIConflationCategory.java +++ /dev/null @@ -1,63 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.mapwithai; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; - -import org.openstreetmap.josm.plugins.mapwithai.io.mapwithai.ConflationSourceReader; -import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig; -import org.openstreetmap.josm.tools.Logging; - -/** - * The conflation category - */ -public final class MapWithAIConflationCategory { - private static final Map> CONFLATION_URLS = new EnumMap<>(MapWithAICategory.class); - private static final String EMPTY_URL = ""; - static { - initialize(); - } - - /** - * Initialize the {@link #CONFLATION_URLS} - */ - public static void initialize() { - CONFLATION_URLS.clear(); - try (ConflationSourceReader reader = new ConflationSourceReader( - MapWithAIConfig.getUrls().getConflationServerJson())) { - reader.parse().ifPresent(CONFLATION_URLS::putAll); - } catch (IOException e) { - Logging.error(e); - } - } - - private MapWithAIConflationCategory() { - // Hide the constructor - } - - /** - * Get a conflation URL for a specific category - * - * @param category The category for conflation - * @return The URL to use for conflation - */ - public static String conflationUrlFor(MapWithAICategory category) { - return CONFLATION_URLS.getOrDefault(category, Collections.emptyList()).stream().findFirst().orElse(EMPTY_URL); - } - - /** - * Add a conflation URL for a specific category - * - * @param category The category for conflation - * @param url The URL to use for conflation - */ - public static void addConflationUrlFor(MapWithAICategory category, String url) { - Collection list = CONFLATION_URLS.computeIfAbsent(category, i -> new ArrayList<>(1)); - list.add(url); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java deleted file mode 100644 index efab8038..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java +++ /dev/null @@ -1,713 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.io.Serial; -import java.io.Serializable; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.openstreetmap.josm.data.StructUtils.StructEntry; -import org.openstreetmap.josm.data.imagery.ImageryInfo; -import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds; -import org.openstreetmap.josm.data.imagery.Shape; -import org.openstreetmap.josm.data.preferences.BooleanProperty; -import org.openstreetmap.josm.data.sources.SourceBounds; -import org.openstreetmap.josm.data.sources.SourceInfo; -import org.openstreetmap.josm.data.sources.SourcePreferenceEntry; -import org.openstreetmap.josm.tools.CheckParameterUtil; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Utils; - -import jakarta.json.Json; -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; -import jakarta.json.stream.JsonParser; - -/** - * The information needed to download external data - */ -public class MapWithAIInfo extends - SourceInfo { - - private static final String PARAMETER_STRING = "parameter"; - private List categories; - private JsonArray parameters; - private Supplier> replacementTagsSupplier; - private Map replacementTags; - private boolean conflate; - private String conflationUrl; - public static final BooleanProperty THIRD_PARTY_CONFLATE = new BooleanProperty("mapwithai.third_party.conflate", - true); - - /** - * The preferred source string for the source. This is added as a source tag on - * the object _and_ is added to the changeset tags. - */ - private String source; - private JsonArray conflationParameters; - private String alreadyConflatedKey; - /** This is for categories that cannot be conflated */ - private List conflationIgnoreCategory; - - /** - * when adding a field, also adapt the: {@link #MapWithAIPreferenceEntry - * MapWithAIPreferenceEntry object} - * {@link MapWithAIPreferenceEntry#MapWithAIPreferenceEntry(MapWithAIInfo) - * MapWithAIPreferenceEntry constructor} - * {@link MapWithAIInfo#MapWithAIInfo(MapWithAIPreferenceEntry) ImageryInfo - * constructor} {@link MapWithAIInfo#MapWithAIInfo MapWithAIInfo constructor} - * {@link MapWithAIInfo#equalsPref equalsPref method} - **/ - public static class MapWithAIPreferenceEntry extends SourcePreferenceEntry { - @StructEntry - String parameters; - @StructEntry - Map replacementTags; - @StructEntry - boolean conflate; - @StructEntry - String conflationUrl; - @StructEntry - String conflationParameters; - @StructEntry - String categories; - @StructEntry - String alreadyConflatedKey; - @StructEntry - String source; - - /** - * Constructs a new empty {@link MapWithAIPreferenceEntry} - */ - public MapWithAIPreferenceEntry() { - // Do nothing - } - - /** - * Constructs a new {@code ImageryPreferenceEntry} from a given - * {@code ImageryInfo}. - * - * @param i The corresponding imagery info - */ - public MapWithAIPreferenceEntry(MapWithAIInfo i) { - super(i); - if (i.parameters != null) { - parameters = i.getParameters().toString(); - } - if (i.conflationParameters != null) { - conflationParameters = i.conflationParameters.toString(); - } - if (i.getReplacementTags() != null) { - this.replacementTags = i.getReplacementTags(); - } - source = i.source; - conflate = i.conflate; - conflationUrl = i.conflationUrl; - if (i.categories != null) { - categories = i.categories.stream().map(MapWithAICategory::getCategoryString) - .collect(Collectors.joining(";")); - } - alreadyConflatedKey = i.alreadyConflatedKey; - if (i.bounds != null && this.shapes != null && this.shapes.length() > Byte.MAX_VALUE) { - List parts = new ArrayList<>(i.bounds.getShapes().size()); - for (Shape s : i.bounds.getShapes()) { - Optional country = CountryUtils.shapeToCountry(s); - if (country.isPresent()) { - if (!parts.contains(country.get())) { - parts.add(country.get()); - } - } else { - parts.add(s.encodeAsString(",")); - } - } - if (!parts.isEmpty()) { - shapes = String.join(";", parts); - } - } - } - - @Override - public String toString() { - final var s = new StringBuilder("MapWithAIPreferenceEntry [name=").append(name); - if (id != null) { - s.append(" id=").append(id); - } - s.append(']'); - return s.toString(); - } - } - - /** - * Compare MapWithAI info - */ - public static class MapWithAIInfoCategoryComparator implements Comparator, Serializable { - - @Serial - private static final long serialVersionUID = -7992892476979310835L; - - @Override - public int compare(MapWithAIInfo o1, MapWithAIInfo o2) { - return (Objects.nonNull(o1.getCategory()) || Objects.nonNull(o2.getCategory()) ? 1 - : Objects.compare(o1.getCategory(), o2.getCategory(), - new MapWithAICategory.DescriptionComparator())); - } - } - - /** - * Create a simple info object - */ - public MapWithAIInfo() { - this((String) null); - } - - /** - * Create a simple info object - * - * @param name The name of the source - */ - public MapWithAIInfo(String name) { - this(name, null); - } - - /** - * Create a simple info object - * - * @param name The name of the source - * @param baseUrl The URL of the source - */ - public MapWithAIInfo(String name, String baseUrl) { - this(name, baseUrl, null); - } - - /** - * Create a simple info object - * - * @param name The name of the source - * @param baseUrl The URL of the source - * @param id The unique ID of the source - */ - public MapWithAIInfo(String name, String baseUrl, String id) { - super(); - setName(name); - setUrl(baseUrl); - setId(id); - setSourceType(MapWithAIType.THIRD_PARTY); - } - - /** - * Constructs a new {@code MapWithAIInfo} with given name, url, id, extended and - * EULA URLs. - * - * @param name The entry name - * @param url The entry URL - * @param type The entry source type. If null, OTHER will be - * used as default - * @param eulaAcceptanceRequired The EULA URL - * @param id tile id - * @throws IllegalArgumentException if type refers to an unknown service type - */ - public MapWithAIInfo(String name, String url, String type, String eulaAcceptanceRequired, String id) { - this(name, url, id); - this.setEulaAcceptanceRequired(eulaAcceptanceRequired); - super.setSourceType(MapWithAIType.fromString(type)); - } - - /** - * Create an info object from a preference entry - * - * @param e The preference entry to copy from - */ - public MapWithAIInfo(MapWithAIPreferenceEntry e) { - this(e.name, e.url, e.id); - CheckParameterUtil.ensureParameterNotNull(e.name, "name"); - CheckParameterUtil.ensureParameterNotNull(e.url, "url"); - setDescription(e.description); - setCookies(e.cookies); - setEulaAcceptanceRequired(e.eula); - if (e.parameters != null) { - try (var parser = Json.createParser(new StringReader(e.parameters))) { - if (parser.hasNext() && JsonParser.Event.START_ARRAY == parser.next()) { - setParameters(parser.getArray()); - } - } - } - setSource(e.source); - if (e.replacementTags != null) { - setReplacementTags(e.replacementTags); - } - setSourceType(MapWithAIType.fromString(e.type)); - if (getSourceType() == null) { - throw new IllegalArgumentException("unknown type"); - } - if (e.bounds != null) { - bounds = new ImageryBounds(e.bounds, ","); - if (e.shapes != null) { - try { - for (String s : e.shapes.split(";", -1)) { - if (s.matches("[\\d,.-]+")) { - bounds.addShape(new Shape(s, ",")); - } else { - CountryUtils.getCountryShape(s).map(SourceBounds::getShapes) - .orElseThrow(IllegalStateException::new).forEach(bounds::addShape); - } - } - } catch (IllegalArgumentException ex) { - Logging.warn(ex); - } - } - } - setAttributionText(e.attribution_text); - setAttributionLinkURL(e.attribution_url); - setPermissionReferenceURL(e.permission_reference_url); - setAttributionImage(e.logo_image); - setAttributionImageURL(e.logo_url); - setDate(e.date); - setTermsOfUseText(e.terms_of_use_text); - setTermsOfUseURL(e.terms_of_use_url); - setCountryCode(e.country_code); - setIcon(e.icon); - setCategory(MapWithAICategory.fromString(e.category)); - if (e.categories != null) { - setAdditionalCategories(Stream.of(e.categories.split(";", -1)).map(MapWithAICategory::fromString).distinct() - .collect(Collectors.toList())); - } - setConflation(e.conflate); - setConflationUrl(e.conflationUrl); - if (e.conflationParameters != null) { - try (JsonParser parser = Json.createParser(new StringReader(e.conflationParameters))) { - if (parser.hasNext() && JsonParser.Event.START_ARRAY == parser.next()) { - setConflationParameters(parser.getArray()); - } - } - } - alreadyConflatedKey = e.alreadyConflatedKey; - } - - /** - * Copy from another info object - * - * @param i The object to copy from - */ - public MapWithAIInfo(MapWithAIInfo i) { - this(i.name, i.url, i.id); - this.alreadyConflatedKey = i.alreadyConflatedKey; - this.attributionImage = i.attributionImage; - this.attributionImageURL = i.attributionImageURL; - this.attributionLinkURL = i.attributionLinkURL; - this.attributionText = i.attributionText; - this.bounds = i.bounds; - this.categories = i.categories; - this.category = i.category; - this.categoryOriginalString = i.categoryOriginalString; - this.conflate = i.conflate; - this.conflationIgnoreCategory = i.conflationIgnoreCategory; - this.conflationUrl = i.conflationUrl; - this.cookies = i.cookies; - this.countryCode = i.countryCode; - this.date = i.date; - this.defaultEntry = i.defaultEntry; - this.defaultLayers = i.defaultLayers; - this.description = i.description; - this.eulaAcceptanceRequired = i.eulaAcceptanceRequired; - this.icon = i.icon; - this.langDescription = i.langDescription; - this.langName = i.langName; - this.maxZoom = i.maxZoom; - this.minZoom = i.minZoom; - this.modTileFeatures = i.modTileFeatures; - this.noTileChecksums = i.noTileChecksums; - this.noTileHeaders = i.noTileHeaders; - this.origName = i.origName; - this.permissionReferenceURL = i.permissionReferenceURL; - this.privacyPolicyURL = i.privacyPolicyURL; - this.setConflationParameters(i.conflationParameters); - this.setCustomHttpHeaders(i.getCustomHttpHeaders()); - this.setIcon(i.icon); - this.setMetadataHeaders(i.getMetadataHeaders()); - this.setParameters(i.getParameters()); - this.setReplacementTags(i.getReplacementTags()); - this.source = i.source; - this.sourceType = i.sourceType; - this.termsOfUseText = i.termsOfUseText; - this.termsOfUseURL = i.termsOfUseURL; - this.tileSize = i.tileSize; - - } - - /** - * Check if this equals the other object as far as the preferences go - * - * @param other The other object to check against - * @return {@code true} if the object is effectively equal - */ - public boolean equalsPref(MapWithAIInfo other) { - if (other == null) { - return false; - } - - // CHECKSTYLE.OFF: BooleanExpressionComplexity - return super.equalsPref(other) && Objects.equals(this.replacementTags, other.replacementTags) - && Objects.equals(this.conflationUrl, other.conflationUrl) - && Objects.equals(this.conflationParameters, other.conflationParameters) - && Objects.equals(this.categories, other.categories) - && Objects.equals(this.alreadyConflatedKey, other.alreadyConflatedKey) - && (this.source == null || other.source == null || Objects.equals(this.source, other.source)) - && compareParameters(this.parameters, other.parameters); - // CHECKSTYLE.ON: BooleanExpressionComplexity - } - - private static boolean compareParameters(JsonArray first, JsonArray second) { - if (Objects.equals(first, second)) { - return true; - } - if (first != null && second != null && first.size() == second.size()) { - for (var value : Utils.filteredCollection(first, JsonObject.class)) { - if (value.containsKey(PARAMETER_STRING) - && value.get(PARAMETER_STRING).getValueType() == JsonValue.ValueType.STRING - && Utils.filteredCollection(second, JsonObject.class).stream() - .filter(obj -> obj.containsKey(PARAMETER_STRING) - && obj.get(PARAMETER_STRING).getValueType() == JsonValue.ValueType.STRING) - .map(obj -> obj.getString(PARAMETER_STRING)) - .noneMatch(obj -> value.getString(PARAMETER_STRING).equals(obj))) { - return false; - } - } - return true; - } - return false; - } - - private static final Map localizedCountriesCache = new HashMap<>(); - static { - localizedCountriesCache.put("", tr("Worldwide")); - } - - /** - * Set the desired source for this object. This is added to source tags on - * objects and is reported via the source changeset tag. - * - * @param source The desired source - */ - public void setSource(String source) { - this.source = source; - } - - /** - * Get the source string to be used for sources on objects and should be used in - * the source changeset tag. - * - * @return The desired source tag, or {@code null}. - */ - public String getSource() { - return this.source; - } - - /** - * Set the MapWithAI category - * - * @param category (i.e., buildings, featured, preview, addresses, etc) - */ - public void setCategory(MapWithAICategory category) { - this.category = category; - } - - /** - * Set the description for the info (may be shown in a mouse hover) - * - * @param description The description for the source - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Get the MapWithAI category - * - * @return The primary category (i.e., buildings, featured, preview, addresses, - * etc) - */ - public MapWithAICategory getCategory() { - return category == null ? MapWithAICategory.OTHER.getDefault() : category; - } - - public void setParameters(JsonArray parameters) { - this.parameters = parameters != null ? Json.createArrayBuilder(parameters).build() : null; - } - - public JsonArray getParameters() { - return parameters != null ? Json.createArrayBuilder(parameters).build() : null; - } - - /** - * Get the parameters as a string (for URL usage) - * - * @return The string to be appended to the url - */ - public List getParametersString() { - return getParametersString(parameters); - } - - private static List getParametersString(JsonArray parameters) { - return parameters == null ? Collections.emptyList() - : parameters.stream().filter(JsonObject.class::isInstance).map(JsonObject.class::cast) - .filter(map -> map.getBoolean("enabled", false)) - .filter(map -> map.containsKey(PARAMETER_STRING)).map(map -> map.getString(PARAMETER_STRING)) - .collect(Collectors.toList()); - - } - - /** - * Get the conflation parameters as a string - * - * @return The conflation parameters to be appended to the url - */ - public List getConflationParameterString() { - return getParametersString(this.conflationParameters); - } - - /** - * Check if this source will have a valid URL when {@link #getUrlExpanded()} is - * called - * - * @return {@code true} if this source will have a valid url - */ - public boolean hasValidUrl() { - return this.url != null || (this.isConflated() && this.conflationUrl != null); - } - - public String getUrlExpanded() { - if (this.isConflated()) { - return getConflationUrl().toString(); - } else { - return getNonConflatedUrl().toString(); - } - } - - private StringBuilder getConflationUrl() { - if (conflationUrl == null) { - return getNonConflatedUrl(); - } - final var sb = new StringBuilder(); - if (this.conflationUrl.contains("{id}") && this.id != null) { - sb.append(conflationUrl.replace("{id}", this.id)); - } else if (this.conflationUrl.contains("{id}")) { - // We need to trigger synchronization. This means that the current download - // may won't be conflated. - // But this should automatically correct the behavior for the next attempt. - final var mwli = MapWithAILayerInfo.getInstance(); - mwli.load(false, () -> { - final var defaultLayer = mwli.getAllDefaultLayers().stream().filter(this::equals).findFirst(); - if (defaultLayer.isPresent()) { - this.id = defaultLayer.get().id; - } else { - final var newInfo = mwli.getAllDefaultLayers().stream() - .filter(layer -> Objects.equals(this.url, layer.url) && Objects.nonNull(layer.id)) - .findFirst().orElse(this); - this.id = newInfo.id; - } - }); - return getNonConflatedUrl(); - } else { - sb.append(conflationUrl); - } - - List parametersString = getConflationParameterString(); - if (!parametersString.isEmpty()) { - sb.append('&').append(String.join("&", parametersString)); - } - return sb; - } - - private StringBuilder getNonConflatedUrl() { - final var sb = new StringBuilder(); - if (url != null && !url.trim().isEmpty()) { - sb.append(url); - if (MapWithAIType.ESRI_FEATURE_SERVER == sourceType) { - if (!url.endsWith("/")) { - sb.append('/'); - } - sb.append("query?geometryType=esriGeometryEnvelope&geometry={bbox}&inSR=4326&f=geojson&outfields=*"); - } - - List parametersString = getParametersString(); - if (!parametersString.isEmpty()) { - sb.append('&').append(String.join("&", parametersString)); - } - } - return sb; - } - - /** - * Set the required replacement tags - * - * @param replacementTags The tags to replace - */ - public void setReplacementTags(Map replacementTags) { - this.replacementTags = replacementTags; - } - - /** - * Set the required replacement tags (as a supplier -- used only to avoid making - * unnecessary requests) - * - * @param replacementTagsSupplier The supplier to get the tags to replace - */ - public void setReplacementTags(final Supplier> replacementTagsSupplier) { - synchronized (this) { - this.replacementTagsSupplier = replacementTagsSupplier; - } - this.replacementTags = null; - } - - /** - * Get the requested tags to replace. These should be run before user requested - * replacements. - * - * @return The required replacement tags - */ - public Map getReplacementTags() { - if (this.replacementTags != null) { - return this.replacementTags; - } - synchronized (this) { - if (this.replacementTagsSupplier != null) { - this.replacementTags = this.replacementTagsSupplier.get(); - this.replacementTagsSupplier = null; - } - } - return this.replacementTags; - } - - /** - * Set whether or not we should perform conflation using the specified - * conflation URL - * - * @param conflation If true, try to use the conflation URL - */ - public void setConflation(boolean conflation) { - this.conflate = conflation; - } - - /** - * Check if this source is being automatically conflated - * - * @return {@code true} if it should be returned already conflated - */ - public boolean isConflated() { - return this.conflate && THIRD_PARTY_CONFLATE.get(); - } - - /** - * Set the URL to use for conflation purposes. - * - * @param conflationUrl Set the conflation url to use. null will disable, but - * you should use {@link MapWithAIInfo#setConflation}. - */ - public void setConflationUrl(String conflationUrl) { - this.conflationUrl = conflationUrl; - } - - /** - * Set any parameters that are required/useful for the conflation URL - * - * @param parameters Set the conflation parameters - */ - public void setConflationParameters(JsonArray parameters) { - this.conflationParameters = parameters; - } - - /** - * Set any additional categories - * - * @param categories The categories to set - */ - public void setAdditionalCategories(List categories) { - this.categories = categories != null ? new ArrayList<>(categories) : null; - } - - /** - * Set additional categories (you can duplicate the primary category here, but - * you shouldn't) - * - * @return Any additional categories - */ - public List getAdditionalCategories() { - return this.categories != null ? Collections.unmodifiableList(this.categories) : Collections.emptyList(); - } - - /** - * Check if this layer has a specified category - * - * @param category The category to look for - * @return {@code true} if this layer has the specified category - */ - public boolean hasCategory(MapWithAICategory category) { - return this.category == category || (this.categories != null && this.categories.contains(category)); - } - - /** - * Set the key that indicates an object is already conflated, and if so, to what - * - * @param key The key returned by the server indicating the conflation object - */ - public void setAlreadyConflatedKey(String key) { - alreadyConflatedKey = key; - } - - /** - * Get the key that indicates an object is already conflated, and if so, to what - * Please note that it may be `true`/`false` instead of an object id. - * - * @return The key returned by the server indicating the conflation object - */ - public String getAlreadyConflatedKey() { - return alreadyConflatedKey; - } - - /** - * Add categories that should not be sent to this MapWithAIInfo's conflation - * service - * - * @param cat The category that cannot be conflated - */ - public void addConflationIgnoreCategory(MapWithAICategory cat) { - if (this.conflationIgnoreCategory == null) { - this.conflationIgnoreCategory = new ArrayList<>(); - } - this.conflationIgnoreCategory.add(cat); - } - - /** - * Get categories that should not be sent to the conflation service - * - * @return An unmodifiable list of non-conflation categories - */ - public List getConflationIgnoreCategory() { - if (this.conflationIgnoreCategory == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(this.conflationIgnoreCategory); - } - - /** - * Get a string usable for toolbars - * - * @return Currently, the name of the source. - */ - public String getToolbarName() { - return this.getName(); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java deleted file mode 100644 index 919fcfb2..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java +++ /dev/null @@ -1,747 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.io.IOException; -import java.io.Serial; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.TreeSet; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.ForkJoinTask; -import java.util.concurrent.RecursiveTask; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.swing.SwingUtilities; - -import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo; -import org.openstreetmap.josm.actions.ExpertToggleAction; -import org.openstreetmap.josm.data.Preferences; -import org.openstreetmap.josm.data.StructUtils; -import org.openstreetmap.josm.data.imagery.ImageryInfo; -import org.openstreetmap.josm.data.preferences.BooleanProperty; -import org.openstreetmap.josm.data.preferences.CachingProperty; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.PleaseWaitRunnable; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.io.CachedFile; -import org.openstreetmap.josm.io.NetworkManager; -import org.openstreetmap.josm.io.imagery.ImageryReader; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo.MapWithAIPreferenceEntry; -import org.openstreetmap.josm.plugins.mapwithai.io.mapwithai.ESRISourceReader; -import org.openstreetmap.josm.plugins.mapwithai.io.mapwithai.MapWithAISourceReader; -import org.openstreetmap.josm.plugins.mapwithai.io.mapwithai.OvertureSourceReader; -import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig; -import org.openstreetmap.josm.spi.preferences.Config; -import org.openstreetmap.josm.tools.ListenerList; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Utils; - -import jakarta.annotation.Nonnull; - -/** - * Manages the list of imagery entries that are shown in the imagery menu. - */ -public class MapWithAILayerInfo { - /** - * A boolean preference used to determine if preview datasets should be shown - */ - public static final CachingProperty SHOW_PREVIEW = new BooleanProperty("mapwithai.sources.preview", false) - .cached(); - - /** Finish listeners */ - private ListenerList finishListenerListenerList = ListenerList.create(); - /** List of all usable layers */ - private final List layers = Collections.synchronizedList(new ArrayList<>()); - /** List of layer ids of all usable layers */ - private final Map layerIds = new HashMap<>(); - private final ListenerList listeners = ListenerList.create(); - /** List of all available default layers */ - static final List defaultLayers = Collections.synchronizedList(new ArrayList<>()); - /** List of all available default layers (including mirrors) */ - static final List allDefaultLayers = Collections.synchronizedList(new ArrayList<>()); - /** List of all layer ids of available default layers (including mirrors) */ - static final Map defaultLayerIds = Collections.synchronizedMap(new HashMap<>()); - - /** The prefix for configuration of the MapWithAI sources */ - public static final String CONFIG_PREFIX = "mapwithai.sources."; - - /** Unique instance -- MUST be after DEFAULT_LAYER_SITES */ - private static MapWithAILayerInfo instance; - - public static MapWithAILayerInfo getInstance() { - if (instance != null) { - return instance; - } - final var finished = new AtomicBoolean(); - synchronized (MapWithAILayerInfo.class) { - if (instance == null) { - instance = new MapWithAILayerInfo(() -> { - synchronized (MapWithAILayerInfo.class) { - finished.set(true); - MapWithAILayerInfo.class.notifyAll(); - } - }); - } else { - finished.set(true); - } - } - // Avoid a deadlock in the EDT. - if (!finished.get() && !SwingUtilities.isEventDispatchThread()) { - var count = 0; - synchronized (MapWithAILayerInfo.class) { - while (!finished.get() && count < 120) { - count++; - try { - MapWithAILayerInfo.class.wait(1000); - } catch (InterruptedException e) { - Logging.error(e); - Thread.currentThread().interrupt(); - } - } - } - } - return instance; - } - - /** - * Returns the list of source layers sites. - * - * @return the list of source layers sites - * @since 7434 - */ - public static Collection getImageryLayersSites() { - return Config.getPref().getList(CONFIG_PREFIX + "layers.sites", - Collections.singletonList(MapWithAIConfig.getUrls().getMapWithAISourcesJson())); - } - - /** - * Set the source sites - * - * @param sites The sites to set - * @return See - * {@link org.openstreetmap.josm.spi.preferences.IPreferences#putList} - */ - public static boolean setImageryLayersSites(Collection sites) { - if (sites == null || sites.isEmpty()) { - return Config.getPref().put(CONFIG_PREFIX + "layers.sites", null); - } else { - return Config.getPref().putList(CONFIG_PREFIX + "layers.sites", new ArrayList<>(sites)); - } - } - - private MapWithAILayerInfo(FinishListener listener) { - load(false, listener); - } - - /** - * Constructs a new {@code ImageryLayerInfo} from an existing one. - * - * @param info info to copy - */ - public MapWithAILayerInfo(MapWithAILayerInfo info) { - layers.addAll(info.layers); - } - - /** - * Clear the lists of layers. - */ - public void clear() { - layers.clear(); - layerIds.clear(); - } - - /** - * Loads the custom as well as default imagery entries. - * - * @param fastFail whether opening HTTP connections should fail fast, see - * {@link ImageryReader#setFastFail(boolean)} - * @param listener A listener to call when loading default entries is finished - */ - public void load(boolean fastFail, FinishListener listener) { - clear(); - final var entries = StructUtils.getListOfStructs(Config.getPref(), CONFIG_PREFIX + "entries", null, - MapWithAIPreferenceEntry.class); - if (entries != null) { - for (MapWithAIPreferenceEntry prefEntry : entries) { - try { - final var i = new MapWithAIInfo(prefEntry); - add(i); - } catch (IllegalArgumentException e) { - Logging.warn("Unable to load imagery preference entry:" + e); - } - } - // Remove a remote control commands in layers - layers.removeIf(i -> i.getUrl().contains("localhost:8111")); - Collections.sort(layers); - } - // Ensure that the cache is initialized prior to running in the fork join pool - // on webstart - if (System.getSecurityManager() != null) { - Logging.trace("MapWithAI loaded: {0}", ESRISourceReader.SOURCE_CACHE.getClass()); - } - loadDefaults(false, MapWithAIDataUtils.getForkJoinPool(), fastFail, listener); - } - - /** - * Loads the available imagery entries. - *

- * The data is downloaded from the JOSM website (or loaded from cache). Entries - * marked as "default" are added to the user selection, if not already present. - * - * @param clearCache if true, clear the cache and start a fresh download. - * @param worker executor service which will perform the loading. If null, - * it should be performed using a {@link PleaseWaitRunnable} - * in the background - * @param fastFail whether opening HTTP connections should fail fast, see - * {@link ImageryReader#setFastFail(boolean)} - * @param listener A listener to call when the everything is done - * @since 12634 - */ - public void loadDefaults(boolean clearCache, ForkJoinPool worker, boolean fastFail, FinishListener listener) { - final var loader = new DefaultEntryLoader(clearCache, fastFail); - if (this.finishListenerListenerList == null) { - this.finishListenerListenerList = ListenerList.create(); - } - boolean running = this.finishListenerListenerList.hasListeners(); - if (listener != null) { - this.finishListenerListenerList.addListener(listener); - } - if (running) { - return; - } - if (worker == null) { - final var pleaseWaitRunnable = new PleaseWaitRunnable(tr("Update default entries")) { - @Override - protected void cancel() { - loader.canceled = true; - } - - @Override - protected void realRun() { - loader.compute(); - } - - @Override - protected void finish() { - loader.finish(); - } - }; - pleaseWaitRunnable.run(); - } else { - worker.execute(loader); - } - } - - /** - * Add a listener for when the data finishes updating - * - * @param finishListener The listener - */ - public void addFinishListener(final FinishListener finishListener) { - if (this.finishListenerListenerList == null) { - finishListener.onFinish(); - } else { - this.finishListenerListenerList.addListener(finishListener); - } - } - - /** - * Loader/updater of the available imagery entries - */ - class DefaultEntryLoader extends RecursiveTask> { - - @Serial - private static final long serialVersionUID = 12550342142551680L; - private final boolean clearCache; - private final boolean fastFail; - private final List newLayers = new ArrayList<>(); - private MapWithAISourceReader reader; - private boolean canceled; - private boolean loadError; - - DefaultEntryLoader(boolean clearCache, boolean fastFail) { - this.clearCache = clearCache; - this.fastFail = fastFail; - } - - protected void cancel() { - canceled = true; - Utils.close(reader); - } - - @Override - public List compute() { - if (this.clearCache) { - ESRISourceReader.SOURCE_CACHE.clear(); - } - // This is literally to avoid allocations on startup - final Preferences preferences; - if (Config.getPref()instanceof Preferences pref) { - preferences = pref; - } else { - preferences = null; - } - try { - if (preferences != null) { - preferences.enableSaveOnPut(false); - } - for (String source : getImageryLayersSites()) { - if (canceled) { - return this.newLayers; - } - loadSource(source); - } - } finally { - if (preferences != null) { - // saveOnPut is pretty much always true - preferences.enableSaveOnPut(true); - MainApplication.worker.execute(() -> { - try { - preferences.save(); - } catch (IOException e) { - // This is highly unlikely to happen - Logging.error(e); - } - }); - } - } - GuiHelper.runInEDTAndWait(this::finish); - return this.newLayers; - } - - protected void loadSource(String source) { - boolean online = !NetworkManager.isOffline(source); - if (clearCache && online) { - CachedFile.cleanup(source); - } - try { - reader = new MapWithAISourceReader(source); - this.reader.setClearCache(this.clearCache); - reader.setFastFail(fastFail); - final var result = reader.parse().orElse(Collections.emptyList()); - // This is called here to "pre-cache" the layer information, to avoid blocking - // the EDT - this.updateEsriLayers(result); - this.updateOvertureLayers(result); - newLayers.addAll(result); - } catch (IOException ex) { - loadError = true; - Logging.log(Logging.LEVEL_ERROR, ex); - } - } - - /** - * Update the esri layer information - * - * @param layers The layers to update - */ - private void updateEsriLayers(@Nonnull final Collection layers) { - final var esriInfo = new ArrayList(300); - for (var layer : layers) { - if (MapWithAIType.ESRI == layer.getSourceType()) { - for (var future : parseEsri(layer)) { - try { - esriInfo.add(future.get()); - } catch (InterruptedException e) { - Logging.error(e); - Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - Logging.error(e); - } - } - } - } - layers.addAll(esriInfo); - } - - /** - * Update the overture layers - * @param layers The layers to iterate through and modify - * @throws IOException If something happens while parsing overture layers - */ - private void updateOvertureLayers(@Nonnull final Collection layers) throws IOException { - final var overtureLayers = new ArrayList(4); - for (var layer : layers) { - if (MapWithAIType.OVERTURE == layer.getSourceType()) { - try (var reader = new OvertureSourceReader(layer)) { - reader.parse().ifPresent(overtureLayers::addAll); - } - } - } - layers.removeIf(layer -> MapWithAIType.OVERTURE == layer.getSourceType()); - layers.addAll(overtureLayers); - } - - protected void finish() { - defaultLayers.clear(); - synchronized (allDefaultLayers) { - allDefaultLayers.clear(); - defaultLayers.addAll(newLayers); - allDefaultLayers.addAll(newLayers); - allDefaultLayers.sort(new MapWithAIInfo.MapWithAIInfoCategoryComparator()); - allDefaultLayers.sort(Comparator.comparing(TileSourceInfo::getName)); - allDefaultLayers.sort(Comparator.comparing(info -> info.getCategory().getDescription())); - allDefaultLayers.sort(Comparator - .comparingInt(info -> -info.getAdditionalCategories().indexOf(MapWithAICategory.FEATURED))); - defaultLayerIds.clear(); - synchronized (defaultLayerIds) { - buildIdMap(allDefaultLayers, defaultLayerIds); - } - } - updateEntriesFromDefaults(!loadError); - buildIdMap(layers, layerIds); - if (!loadError && !defaultLayerIds.isEmpty()) { - dropOldEntries(); - } - final var listenerList = MapWithAILayerInfo.this.finishListenerListenerList; - MapWithAILayerInfo.this.finishListenerListenerList = null; - Config.getPref().putLong("mapwithai.layerinfo.lastupdated", Instant.now().getEpochSecond()); - if (listenerList != null) { - listenerList.fireEvent(FinishListener::onFinish); - } - } - - /** - * Parse an Esri layer - * - * @param layer The layer to parse - * @return The Feature Servers for the ESRI layer - */ - private Collection> parseEsri(MapWithAIInfo layer) { - try { - return new ESRISourceReader(layer).parse(); - } catch (IOException e) { - Logging.error(e); - } - return Collections.emptyList(); - } - - /** - * Build the mapping of unique ids to {@link ImageryInfo}s. - * - * @param lst input list - * @param idMap output map - */ - private void buildIdMap(List lst, Map idMap) { - idMap.clear(); - final var notUnique = new HashSet(); - for (var i : lst) { - if (i.getId() != null) { - if (idMap.containsKey(i.getId())) { - notUnique.add(i.getId()); - Logging.error("Id ''{0}'' is not unique - used by ''{1}'' and ''{2}''!", i.getId(), i.getName(), - idMap.get(i.getId()).getName()); - continue; - } - idMap.put(i.getId(), i); - } - } - for (var i : notUnique) { - idMap.remove(i); - } - } - } - - /** - * Update user entries according to the list of default entries. - * - * @param dropold if true old entries should be removed - * @since 11706 - */ - public void updateEntriesFromDefaults(boolean dropold) { - // add new default entries to the user selection - var changed = false; - final var knownDefaults = new TreeSet<>(Config.getPref().getList(CONFIG_PREFIX + "layers.default")); - final var newKnownDefaults = new TreeSet(); - synchronized (defaultLayers) { - for (var def : defaultLayers) { - if (def.isDefaultEntry()) { - var isKnownDefault = false; - for (var entry : knownDefaults) { - if (entry.equals(def.getId())) { - isKnownDefault = true; - newKnownDefaults.add(entry); - knownDefaults.remove(entry); - break; - } else if (isSimilar(entry, def.getUrl())) { - isKnownDefault = true; - if (def.getId() != null) { - newKnownDefaults.add(def.getId()); - } - knownDefaults.remove(entry); - break; - } - } - var isInUserList = false; - if (!isKnownDefault) { - if (def.getId() != null) { - newKnownDefaults.add(def.getId()); - for (var i : layers) { - if (isSimilar(def, i)) { - isInUserList = true; - break; - } - } - } else { - Logging.error("Default imagery ''{0}'' has no id. Skipping.", def.getName()); - } - } - if (!isKnownDefault && !isInUserList) { - add(new MapWithAIInfo(def)); - changed = true; - } - } - } - } - if (!dropold && !knownDefaults.isEmpty()) { - newKnownDefaults.addAll(knownDefaults); - } - Config.getPref().putList(CONFIG_PREFIX + "layers.default", new ArrayList<>(newKnownDefaults)); - - // automatically update user entries with same id as a default entry - for (var i = 0; i < layers.size(); i++) { - final var info = layers.get(i); - if (info.getId() == null) { - continue; - } - final var matchingDefault = defaultLayerIds.get(info.getId()); - if (matchingDefault != null && !matchingDefault.equalsPref(info)) { - layers.set(i, matchingDefault); - Logging.info(tr("Update imagery ''{0}''", info.getName())); - changed = true; - } - } - - if (changed) { - MainApplication.worker.execute(this::save); - } - } - - /** - * Drop entries with Id which do no longer exist (removed from defaults). - * - * @since 11527 - */ - public void dropOldEntries() { - final var drop = new ArrayList(); - - for (var info : layerIds.entrySet()) { - if (!defaultLayerIds.containsKey(info.getKey())) { - remove(info.getValue()); - drop.add(info.getKey()); - Logging.info(tr("Drop old imagery ''{0}''", info.getValue().getName())); - } - } - - if (!drop.isEmpty()) { - for (var id : drop) { - layerIds.remove(id); - } - save(); - } - } - - private static boolean isSimilar(MapWithAIInfo iiA, MapWithAIInfo iiB) { - if (iiA == null || iiB == null) { - return false; - } - if (iiA.getId() != null && iiB.getId() != null) { - return iiA.getId().equals(iiB.getId()); - } - return isSimilar(iiA.getUrl(), iiB.getUrl()); - } - - // some additional checks to respect extended URLs in preferences (legacy - // workaround) - private static boolean isSimilar(String a, String b) { - return Objects.equals(a, b) - || (a != null && b != null && !a.isEmpty() && !b.isEmpty() && (a.contains(b) || b.contains(a))); - } - - /** - * Add a new imagery entry. - * - * @param info imagery entry to add - */ - public void add(MapWithAIInfo info) { - layers.add(info); - this.listeners.fireEvent(l -> l.changeEvent(info)); - } - - /** - * Remove an imagery entry. - * - * @param info imagery entry to remove - */ - public void remove(MapWithAIInfo info) { - layers.remove(info); - this.listeners.fireEvent(l -> l.changeEvent(info)); - } - - /** - * Save the list of imagery entries to preferences. - */ - public synchronized void save() { - final var entries = new ArrayList(); - synchronized (layers) { - for (var info : layers) { - entries.add(new MapWithAIPreferenceEntry(info)); - } - } - StructUtils.putListOfStructs(Config.getPref(), CONFIG_PREFIX + "entries", entries, - MapWithAIPreferenceEntry.class); - } - - /** - * List of usable layers - * - * @return unmodifiable list containing usable layers - */ - public List getLayers() { - return Collections.unmodifiableList(filterPreview(layers)); - } - - /** - * List of available default layers - * - * @return unmodifiable list containing available default layers - */ - public List getDefaultLayers() { - return Collections.unmodifiableList(filterPreview(defaultLayers)); - } - - /** - * List of all available default layers (including mirrors) - * - * @return unmodifiable list containing available default layers - * @since 11570 - */ - public List getAllDefaultLayers() { - return Collections.unmodifiableList(filterPreview(allDefaultLayers)); - } - - /** - * Remove preview layers, if {@link #SHOW_PREVIEW} is not {@code true} - * - * @param layers The layers to filter - * @return The layers without any preview layers, if {@link #SHOW_PREVIEW} is - * not {@code true}. - */ - private static List filterPreview(List layers) { - final var newList = new ArrayList<>(layers); - newList.removeIf(MapWithAILayerInfo::isFiltered); - return newList; - } - - /** - * Check if the layer should be filtered out - * - * @param info The layer to check - * @return {@code true} if the layer should be filtered - */ - public static boolean isFiltered(MapWithAIInfo info) { - if (info == null || !info.hasValidUrl()) { - return true; - } - if (ExpertToggleAction.isExpert() && Boolean.TRUE.equals(SHOW_PREVIEW.get())) { - return false; - } - return info.hasCategory(MapWithAICategory.PREVIEW); - } - - /** - * Add a data source - * - * @param info The source to add - */ - public static void addLayer(MapWithAIInfo info) { - instance.add(info); - instance.save(); - } - - /** - * Add multiple data sources - * - * @param infos The sources to add - */ - public static void addLayers(Collection infos) { - infos.forEach(instance::add); - instance.save(); - Collections.sort(instance.layers); - } - - /** - * Get unique id for ImageryInfo. - *

- * This takes care, that no id is used twice (due to a user error) - * - * @param info the ImageryInfo to look up - * @return null, if there is no id or the id is used twice, the corresponding id - * otherwise - */ - public String getUniqueId(MapWithAIInfo info) { - if (info != null && info.getId() != null && info.equals(layerIds.get(info.getId()))) { - return info.getId(); - } - return null; - } - - /** - * Returns imagery layer info for the given id. - * - * @param id imagery layer id. - * @return imagery layer info for the given id, or {@code null} - * @since 13797 - */ - public MapWithAIInfo getLayer(String id) { - return layerIds.get(id); - } - - /** - * Listen for the data source info to finish downloading - */ - public interface FinishListener { - /** - * Called when information is finished loading - */ - void onFinish(); - } - - /** - * Add a listener that is called on layer change events. Only fires on single - * add/remove events. - * - * @param listener The listener to be called. - */ - public void addListener(LayerChangeListener listener) { - this.listeners.addListener(listener); - } - - /** - * An interface to tell listeners what info object has changed - * - * @author Taylor Smock - * - */ - public interface LayerChangeListener { - /** - * Fired when an info object has been added/removed to the layer list - * - * @param modified A MapWithAIInfo object that has been removed or added to the - * layers - */ - void changeEvent(MapWithAIInfo modified); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIType.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIType.java deleted file mode 100644 index bde9e408..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIType.java +++ /dev/null @@ -1,53 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.mapwithai; - -import org.openstreetmap.josm.data.sources.ISourceType; - -import jakarta.annotation.Nonnull; - -/** - * Type of MapWithAI entry - * - * @author Taylor Smock - */ -public enum MapWithAIType implements ISourceType { - FACEBOOK("facebook"), THIRD_PARTY("thirdParty"), ESRI("esri"), ESRI_FEATURE_SERVER( - "esriFeatureServer"), MAPBOX_VECTOR_TILE("mvt"), PMTILES("pmtiles"), OVERTURE("overture"); - - private final String typeString; - - MapWithAIType(String typeString) { - this.typeString = typeString; - } - - @Override - public String getTypeString() { - return typeString; - } - - /** - * Get the type from a string - * - * @param s The string to parse - * @return The type - */ - @Nonnull - public static MapWithAIType fromString(String s) { - for (MapWithAIType type : MapWithAIType.values()) { - if (type.getTypeString().equals(s)) { - return type; - } - } - return MapWithAIType.THIRD_PARTY; - } - - @Override - public MapWithAIType getDefault() { - return THIRD_PARTY; - } - - @Override - public MapWithAIType getFromString(String s) { - return fromString(s); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/PreConflatedDataUtils.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/PreConflatedDataUtils.java deleted file mode 100644 index ef286043..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/PreConflatedDataUtils.java +++ /dev/null @@ -1,96 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.mapwithai; - -import org.openstreetmap.josm.command.ChangePropertyKeyCommand; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAILayer; -import org.openstreetmap.josm.spi.preferences.Config; -import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent; -import org.openstreetmap.josm.spi.preferences.PreferenceChangedListener; -import org.openstreetmap.josm.tools.Destroyable; - -/** - * Show/hide conflated objects. This depends upon the server indicating that an - * object is conflated. - * - * @author Taylor Smock - * - */ -public class PreConflatedDataUtils implements PreferenceChangedListener, Destroyable { - /** The preference key that determines if objects are shown/hidden */ - protected static final String PREF_KEY = "mapwithai.conflated.hide"; - /** - * If this config value is true, completely hide the conflated item instead of - * just greying it out - */ - protected static final String PREF_KEY_FULL = PREF_KEY + ".full"; - /** - * The key added to objects to indicate that they have been conflated. May show - * object or be true/false - */ - private static final String CONFLATED_KEY = "mapwithai:conflated"; - - /** - * Create a new util object - */ - public PreConflatedDataUtils() { - Config.getPref().addKeyPreferenceChangeListener(PREF_KEY, this); - Config.getPref().addKeyPreferenceChangeListener(PREF_KEY_FULL, this); - } - - /** - * Despite the name, this method changes the conflated tag to a standard tag, - * and then hides the data. - * - * @param dataSet The dataset to look through - * @param info The info with the key to use to convert to the - * mapwithai:conflated tag - */ - public static void removeConflatedData(DataSet dataSet, MapWithAIInfo info) { - if (info != null && info.getAlreadyConflatedKey() != null && !info.getAlreadyConflatedKey().trim().isEmpty()) { - String key = info.getAlreadyConflatedKey(); - dataSet.allPrimitives().stream().filter(p -> p.hasKey(key)) - .forEach(p -> new ChangePropertyKeyCommand(p, key, getConflatedKey()).executeCommand()); - hideConflatedData(dataSet); - } - } - - /** - * Hide conflated data. - * - * @param dataSet The dataset to show/hide data in - */ - public static void hideConflatedData(DataSet dataSet) { - boolean hide = Config.getPref().getBoolean(PREF_KEY, true); - boolean fullHide = Config.getPref().getBoolean(PREF_KEY_FULL, false); - dataSet.allPrimitives().stream().filter(p -> p.hasKey(getConflatedKey())).forEach(p -> { - if (hide) { - p.setDisabledState(fullHide); - } else { - p.unsetDisabledState(); - } - }); - } - - @Override - public void preferenceChanged(PreferenceChangeEvent e) { - MainApplication.getLayerManager().getLayersOfType(MapWithAILayer.class).stream().map(MapWithAILayer::getDataSet) - .distinct().forEach(PreConflatedDataUtils::hideConflatedData); - } - - @Override - public void destroy() { - Config.getPref().removeKeyPreferenceChangeListener(PREF_KEY, this); - Config.getPref().removeKeyPreferenceChangeListener(PREF_KEY_FULL, this); - } - - /** - * Get the key used to indicate that an object is already conflated. - * - * @return the conflatedKey - */ - public static String getConflatedKey() { - return CONFLATED_KEY; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/ConnectingNodeInformationTest.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/ConnectingNodeInformationTest.java deleted file mode 100644 index c11458b3..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/ConnectingNodeInformationTest.java +++ /dev/null @@ -1,88 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.validation.tests; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.lang.reflect.InvocationTargetException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.command.ChangePropertyCommand; -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.DownloadPolicy; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.UploadPolicy; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.data.validation.Severity; -import org.openstreetmap.josm.data.validation.Test; -import org.openstreetmap.josm.data.validation.TestError; -import org.openstreetmap.josm.gui.progress.ProgressMonitor; -import org.openstreetmap.josm.plugins.mapwithai.commands.AbstractConflationCommand; -import org.openstreetmap.josm.plugins.mapwithai.commands.CreateConnectionsCommand; -import org.openstreetmap.josm.tools.Logging; - -/** - * Ensure that no conflation keys remain - * - * @author Taylor Smock - */ -public class ConnectingNodeInformationTest extends Test { - private static final int ERROR_CODE = 827_277_536; - Map badTags; - - /** - * Create a new test instance - */ - public ConnectingNodeInformationTest() { - super(tr("Left over conflation information (MapWithAI)"), - tr("Checks conflation keys that should not exist in OpenStreetMap.")); - } - - @Override - public void startTest(ProgressMonitor monitor) { - super.startTest(monitor); - badTags = new HashMap<>(); - CreateConnectionsCommand.getConflationCommands().forEach(clazz -> { - try { - AbstractConflationCommand command = clazz.getConstructor(DataSet.class).newInstance(new DataSet()); - if (command.keyShouldNotExistInOSM()) { - badTags.put(command.getKey(), null); - } - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | NoSuchMethodException | SecurityException e) { - Logging.error(e); - } - }); - } - - @Override - public void visit(Relation relation) { - checkTags(relation); - } - - @Override - public void visit(Way way) { - checkTags(way); - } - - @Override - public void visit(Node node) { - checkTags(node); - } - - private void checkTags(OsmPrimitive prim) { - DataSet ds = prim.getDataSet(); - if (UploadPolicy.BLOCKED != ds.getUploadPolicy() && DownloadPolicy.BLOCKED != ds.getDownloadPolicy() - && prim.hasKey(badTags.keySet().toArray(new String[0]))) { - errors.add(TestError.builder(this, Severity.ERROR, ERROR_CODE).primitives(prim) - .message(tr("Don''t leave conflation keys in the data {0}", - badTags.keySet().stream().filter(prim::hasKey).collect(Collectors.toList()))) - .fix(() -> new ChangePropertyCommand(Collections.singleton(prim), badTags)).build()); - } - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/RoutingIslandsTest.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/RoutingIslandsTest.java deleted file mode 100644 index d5f428c0..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/RoutingIslandsTest.java +++ /dev/null @@ -1,533 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.validation.tests; - -import static org.openstreetmap.josm.tools.I18n.marktr; -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.BiPredicate; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.TagMap; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper; -import org.openstreetmap.josm.data.validation.Severity; -import org.openstreetmap.josm.data.validation.Test; -import org.openstreetmap.josm.data.validation.TestError; -import org.openstreetmap.josm.gui.progress.ProgressMonitor; -import org.openstreetmap.josm.plugins.mapwithai.tools.Access; -import org.openstreetmap.josm.spi.preferences.Config; -import org.openstreetmap.josm.tools.Pair; - -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; - -/** - * A test for routing islands - * - * @author Taylor Smock - * @since xxx - */ -public class RoutingIslandsTest extends Test { - - private static final Map SEVERITY_MAP = new HashMap<>(); - /** - * A map of <direction, <mode, direction:mode>> to reduce concat - * costs - */ - private static final Map> DIRECTION_MAP = new HashMap<>(3); - /** The code for the routing island validation test */ - public static final int ROUTING_ISLAND = 55000; - /** The code for ways that are not connected to other ways, and are routable */ - public static final int LONELY_WAY = ROUTING_ISLAND + 1; - static { - SEVERITY_MAP.put(ROUTING_ISLAND, Severity.OTHER); - SEVERITY_MAP.put(LONELY_WAY, Severity.ERROR); - } - - private static final String HIGHWAY = "highway"; - private static final String WATERWAY = "waterway"; - private static final List IGNORE_TAGS_HIGHWAY = Arrays.asList("services", "rest_area", "platform"); - private static final List IGNORE_TAGS_WATERWAY = Arrays.asList("services", "rest_area", "dam"); - - /** - * This is mostly as a sanity check, and to avoid infinite recursion (shouldn't - * happen, but still) - */ - private static final int MAX_LOOPS = 1000; - /** Highways to check for routing connectivity */ - private Set potentialHighways; - /** Waterways to check for routing connectivity */ - private Set potentialWaterways; - - /** - * Constructs a new {@code RightAngleBuildingTest} test. - */ - public RoutingIslandsTest() { - super(tr("Routing islands (MapWithAI)"), tr("Checks for roads that cannot be reached or left.")); - super.setPartialSelection(false); - } - - @Override - public void startTest(ProgressMonitor monitor) { - super.startTest(monitor); - potentialHighways = new HashSet<>(); - potentialWaterways = new HashSet<>(); - } - - @Override - public void endTest() { - Access.AccessTags.getByTransportType(Access.AccessTags.LAND_TRANSPORT_TYPE).forEach(mode -> { - runTest(mode.getKey(), potentialHighways); - progressMonitor.setCustomText(mode.getKey()); - }); - Access.AccessTags.getByTransportType(Access.AccessTags.WATER_TRANSPORT_TYPE).forEach(mode -> { - progressMonitor.setCustomText(mode.getKey()); - runTest(mode.getKey(), potentialWaterways); - }); - super.endTest(); - } - - @Override - public void visit(Way way) { - if (way.isUsable() - && ((way.hasKey(HIGHWAY) && !IGNORE_TAGS_HIGHWAY.contains(way.get(HIGHWAY))) - || (way.hasKey(WATERWAY) && !IGNORE_TAGS_WATERWAY.contains(way.get(WATERWAY)))) - && way.getNodes().stream().anyMatch(node -> way.getDataSet().getDataSourceBounds().stream() - .anyMatch(source -> source.contains(node.getCoor())))) { - if ((way.hasKey(HIGHWAY) || way.hasKey(WATERWAY)) && way.getNodes().stream() - .flatMap(node -> node.getReferrers().stream()).distinct().allMatch(way::equals) - && way.getNodes().stream().noneMatch(Node::isOutsideDownloadArea)) { - errors.add(TestError.builder(this, SEVERITY_MAP.get(LONELY_WAY), LONELY_WAY).primitives(way) - .message(tr("MapWithAI (experimental)"), marktr("Routable way not connected to other ways")) - .build()); - } else if ((ValidatorPrefHelper.PREF_OTHER.get() || ValidatorPrefHelper.PREF_OTHER_UPLOAD.get() - || Severity.OTHER != SEVERITY_MAP.get(ROUTING_ISLAND)) && !isBeforeUpload) { - if (way.hasKey(HIGHWAY) && !IGNORE_TAGS_HIGHWAY.contains(way.get(HIGHWAY))) { - potentialHighways.add(way); - } - if (way.hasKey(WATERWAY) && !IGNORE_TAGS_WATERWAY.contains(way.get(WATERWAY))) { - potentialWaterways.add(way); - } - } - } - } - - private void runTest(String currentTransportMode, Collection potentialWays) { - Set incomingWays = new HashSet<>(); - Set outgoingWays = new HashSet<>(); - findConnectedWays(currentTransportMode, potentialWays, incomingWays, outgoingWays); - Collection realPotentialWays = (incomingWays.isEmpty() || outgoingWays.isEmpty()) - ? expandNetwork(potentialWays) - : potentialWays; - - if (incomingWays.isEmpty() || outgoingWays.isEmpty()) { - findConnectedWays(currentTransportMode, realPotentialWays, incomingWays, outgoingWays); - } - runGenericTest(currentTransportMode, realPotentialWays, incomingWays, outgoingWays); - - } - - /** - * Expand a network from an initial selection - * - * @param initial The initial collection of ways - * @return An expanded collection of ways, which should be all connected ways - * that allow the current transport mode. - */ - private static Collection expandNetwork(Collection initial) { - Collection connected = initial.stream().flatMap(way -> way.getNodes().stream()) - .flatMap(node -> node.getReferrers().stream()).filter(Way.class::isInstance).map(Way.class::cast) - .collect(Collectors.toSet()); - if (connected.containsAll(initial) && initial.containsAll(connected)) { - return connected; - } - return expandNetwork(connected); - } - - /** - * This test is run when there are known incoming/outgoing ways - * - * @param currentTransportMode The current transport mode - * @param potentialWays The ways to check - * @param incomingWays The incoming ways - * @param outgoingWays The outgoing ways - */ - private void runGenericTest(String currentTransportMode, Collection potentialWays, - Collection incomingWays, Collection outgoingWays) { - Set toIgnore = potentialWays.stream() - .filter(way -> incomingWays.contains(way) || outgoingWays.contains(way)) - .filter(way -> !Access.getPositiveAccessValues().contains( - getDefaultAccessTags(way).getOrDefault(currentTransportMode, Access.AccessTags.NO.getKey()))) - .collect(Collectors.toSet()); - incomingWays.removeAll(toIgnore); - outgoingWays.removeAll(toIgnore); - - checkForUnconnectedWays(incomingWays, outgoingWays, currentTransportMode); - List>> problematic = collectConnected(potentialWays.stream() - .filter(way -> !incomingWays.contains(way) || !outgoingWays.contains(way)) - .filter(way -> Access.getPositiveAccessValues().contains( - getDefaultAccessTags(way).getOrDefault(currentTransportMode, Access.AccessTags.NO.getKey()))) - .collect(Collectors.toSet())) - .stream() - .map(way -> new Pair<>( - (incomingWays.containsAll(way) ? marktr("outgoing") : marktr("incoming")), way)) - .toList(); - createErrors(problematic, currentTransportMode); - } - - /** - * Find ways that may be connected to the wider network - * - * @param currentTransportMode The current mode of transport - * @param potentialWays The ways to check for connections - * @param incomingWays A collection that will have incoming ways after - * this method is called - * @param outgoingWays A collection that will have outgoing ways after - * this method is called - */ - private static void findConnectedWays(String currentTransportMode, Collection potentialWays, - Collection incomingWays, Collection outgoingWays) { - potentialWays.stream().filter(Way::isUsable).filter(Way::isOutsideDownloadArea).forEach(way -> { - final var firstNode = firstNode(way, currentTransportMode); - final var lastNode = lastNode(way, currentTransportMode); - final var isOneway = isOneway(way, currentTransportMode); - if (firstNode != null && firstNode.isOutsideDownloadArea()) { - incomingWays.add(way); - } - if (lastNode != null && lastNode.isOutsideDownloadArea()) { - outgoingWays.add(way); - } - if (isOneway != null && isOneway == 0 && firstNode != null && lastNode != null - && (firstNode.isOutsideDownloadArea() || lastNode.isOutsideDownloadArea())) { - incomingWays.add(way); - outgoingWays.add(way); - } - }); - } - - /** - * Take a collection of ways and modify it so that it is a list of connected - * ways - * - * @param ways A collection of ways that may or may not be connected - * @return a list of sets of ways that are connected - */ - private static List> collectConnected(Collection ways) { - final var collected = new ArrayList>(); - final var listOfWays = new ArrayList<>(ways); - final var maxLoop = Config.getPref().getInt("validator.routingislands.maxrecursion", MAX_LOOPS); - for (var i = 0; i < listOfWays.size(); i++) { - final var initial = listOfWays.get(i); - Set connected = new HashSet<>(); - connected.add(initial); - var loopCounter = 0; - while (!getConnected(connected) && loopCounter < maxLoop) { - loopCounter++; - } - if (listOfWays.removeAll(connected)) { - /* - * Not an issue -- this ensures that everything is accounted for, only triggers - * when ways removed - */ - i--; // NOSONAR NOPMD - } - collected.add(connected); - } - return collected; - } - - private static boolean getConnected(Collection ways) { - TagMap defaultAccess = getDefaultAccessTags(ways.iterator().next()); - return ways.addAll(ways.stream().flatMap(way -> way.getNodes().stream()) - .flatMap(node -> node.getReferrers().stream()).filter(Way.class::isInstance).map(Way.class::cast) - .filter(way -> getDefaultAccessTags(way).equals(defaultAccess)).collect(Collectors.toSet())); - } - - /** - * Create errors for a problematic way - * - * @param problematic The set of problematic ways (Pairs are - * <incoming/outgoing, Set<Connected ways with same - * issue>>) - * @param mode The transport mode - */ - private void createErrors(List>> problematic, String mode) { - for (Pair> ways : problematic) { - errors.add( - TestError.builder(this, SEVERITY_MAP.getOrDefault(ROUTING_ISLAND, Severity.OTHER), ROUTING_ISLAND) - .message(tr("MapWithAI (experimental)"), marktr("Routing island"), "{1}: {0}", tr(ways.a), - mode == null ? marktr("default") : mode) - .primitives(ways.b).build()); - } - } - - /** - * Check for unconnected ways - * - * @param incoming The current incoming ways (will be modified) - * @param outgoing The current outgoing ways (will be modified) - * @param currentTransportMode The transport mode we are investigating (may be - * {@code null}) - */ - public static void checkForUnconnectedWays(Collection incoming, Collection outgoing, - String currentTransportMode) { - var loopCount = 0; - var maxLoops = Config.getPref().getInt("validator.routingislands.maxrecursion", MAX_LOOPS); - do { - loopCount++; - } while (loopCount <= maxLoops && getWaysFor(incoming, currentTransportMode, - (way, oldWay) -> oldWay.containsNode(firstNode(way, currentTransportMode)) - && checkAccessibility(oldWay, way, currentTransportMode))); - loopCount = 0; - do { - loopCount++; - } while (loopCount <= maxLoops && getWaysFor(outgoing, currentTransportMode, - (way, oldWay) -> oldWay.containsNode(lastNode(way, currentTransportMode)) - && checkAccessibility(oldWay, way, currentTransportMode))); - } - - private static boolean getWaysFor(Collection directional, String currentTransportMode, - BiPredicate predicate) { - Set toAdd = new HashSet<>(); - for (Way way : directional) { - for (Node node : way.getNodes()) { - Set referrers = node.getReferrers(true).stream().filter(Way.class::isInstance).map(Way.class::cast) - .filter(tWay -> !directional.contains(tWay)).collect(Collectors.toSet()); - for (Way tWay : referrers) { - Integer oneWay = isOneway(tWay, currentTransportMode); - if ((oneWay != null && oneWay == 0) || predicate.test(tWay, way) || tWay.isClosed()) { - toAdd.add(tWay); - } - } - } - } - return directional.addAll(toAdd); - } - - /** - * Check if I can get to way to from way from (currently doesn't work with via - * ways) - * - * @param from The from way - * @param to The to way - * @param currentTransportMode The specific transport mode to check - * @return {@code true} if the to way can be accessed from the from way TODO - * clean up and work with via ways - */ - public static boolean checkAccessibility(Way from, Way to, String currentTransportMode) { - var isAccessible = true; - - List relations = from.getReferrers().stream().distinct().filter(Relation.class::isInstance) - .map(Relation.class::cast).filter(relation -> "restriction".equals(relation.get("type"))).toList(); - for (Relation relation : relations) { - if (((relation.hasKey("except") && relation.get("except").contains(currentTransportMode)) - || (currentTransportMode == null || currentTransportMode.trim().isEmpty())) - && relation.getMembersFor(Collections.singleton(from)).stream() - .anyMatch(member -> "from".equals(member.getRole())) - && relation.getMembersFor(Collections.singleton(to)).stream() - .anyMatch(member -> "to".equals(member.getRole()))) { - isAccessible = false; - } - } - return isAccessible; - } - - /** - * Check if a node connects to the outside world - * - * @param node The node to check - * @return true if outside download area, connects to an aeroport, or a water - * transport - */ - public static Boolean outsideConnections(Node node) { - return node.isOutsideDownloadArea() || node.hasTag("amenity", "parking_entrance", "parking", "parking_space", - "motorcycle_parking", "ferry_terminal"); - } - - /** - * Check if a way is oneway for a specific transport type - * - * @param way The way to look at - * @param transportType The specific transport type - * @return See {@link Way#isOneway} (but may additionally return {@code null} if - * the transport type cannot route down that way) - */ - @Nullable - public static Integer isOneway(@Nonnull Way way, @Nullable String transportType) { - if (transportType == null || transportType.trim().isEmpty()) { - return way.isOneway(); - } - String forward = transportType.concat(":forward"); - String backward = transportType.concat(":backward"); - boolean possibleForward = "yes".equals(way.get(forward)) || (!way.hasKey(forward) && way.isOneway() != -1); - boolean possibleBackward = "yes".equals(way.get(backward)) || (!way.hasKey(backward) && way.isOneway() != 1); - if (transportType.equals(Access.AccessTags.FOOT.getKey()) && !"footway".equals(way.get(HIGHWAY)) - && !way.hasTag("foot:forward") && !way.hasTag("foot:backward")) { - /* - * Foot is almost never oneway, especially on generic road types. There are some - * cases on mountain paths. - */ - return 0; - } - if (possibleForward && !possibleBackward) { - return 1; - } else if (!possibleForward && possibleBackward) { - return -1; - } else if (!possibleBackward) { - return null; - } - return 0; - } - - /** - * Get the first node of a way respecting the oneway for a transport type - * - * @param way The way to get the node from - * @param transportType The transport type - * @return The first node for the specified transport type, or null if it is not - * routable - */ - public static Node firstNode(Way way, String transportType) { - Integer oneway = isOneway(way, transportType); - Node node = Integer.valueOf(-1).equals(oneway) ? way.lastNode() : way.firstNode(); - - Map accessValues = getDefaultAccessTags(way); - boolean accessible = Access.getPositiveAccessValues() - .contains(accessValues.getOrDefault(transportType, Access.AccessTags.NO.getKey())); - return (transportType == null || accessible) ? node : null; - } - - /** - * Get the last node of a way respecting the oneway for a transport type - * - * @param way The way to get the node from - * @param transportType The transport type - * @return The last node for the specified transport type, or the last node of - * the way, or null if it is not routable - */ - public static Node lastNode(Way way, String transportType) { - Integer oneway = isOneway(way, transportType); - Node node = Integer.valueOf(-1).equals(oneway) ? way.firstNode() : way.lastNode(); - Map accessValues = getDefaultAccessTags(way); - boolean accessible = Access.getPositiveAccessValues() - .contains(accessValues.getOrDefault(transportType, Access.AccessTags.NO.getKey())); - return (transportType == null || accessible) ? node : null; - } - - /** - * Get the default access tags for a primitive - * - * @param primitive The primitive to get access tags for - * @return The map of access tags to access - */ - public static TagMap getDefaultAccessTags(OsmPrimitive primitive) { - final var access = new TagMap(); - final TagMap tags; - if (primitive.hasKey(HIGHWAY)) { - tags = getDefaultHighwayAccessTags(primitive.getKeys()); - } else if (primitive.hasKey(WATERWAY)) { - tags = getDefaultWaterwayAccessTags(primitive.getKeys()); - } else { - tags = new TagMap(); - } - tags.putAll(Access.expandAccessValues(tags)); - - for (String direction : Arrays.asList("", "forward:", "backward:")) { - access.putAll(Access.getTransportModes().stream().map(mode -> getCachedDirectionMode(direction, mode)) - .filter(tags::containsKey).collect(Collectors.toMap(mode -> mode, tags::get))); - } - return access; - } - - private static String getCachedDirectionMode(String direction, String mode) { - DIRECTION_MAP.computeIfAbsent(direction, d -> new HashMap<>(Access.getTransportModes().size())); - final var directionMap = DIRECTION_MAP.get(direction); - directionMap.computeIfAbsent(mode, direction::concat); - return directionMap.get(mode); - } - - private static TagMap getDefaultWaterwayAccessTags(TagMap tags) { - if ("river".equals(tags.get(WATERWAY))) { - tags.putIfAbsent("boat", Access.AccessTags.YES.getKey()); - } - return tags; - } - - private static TagMap getDefaultHighwayAccessTags(TagMap tags) { - String highway = tags.get(HIGHWAY); - - if (tags.containsKey("sidewalk") && !tags.get("sidewalk").equals(Access.AccessTags.NO.getKey())) { - tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.YES.getKey()); - } - - if (tags.keySet().stream() - .anyMatch(str -> str.contains("cycleway") && !Access.AccessTags.NO.getKey().equals(tags.get(str)))) { - tags.putIfAbsent(Access.AccessTags.BICYCLE.getKey(), Access.AccessTags.YES.getKey()); - } - - if ("residential".equals(highway)) { - tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); - tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.YES.getKey()); - tags.putIfAbsent(Access.AccessTags.BICYCLE.getKey(), Access.AccessTags.YES.getKey()); - } else if (Arrays.asList("service", "unclassified", "tertiary", "tertiary_link").contains(highway)) { - tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); - } else if (Arrays.asList("secondary", "secondary_link").contains(highway)) { - tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); - } else if (Arrays.asList("primary", "primary_link").contains(highway)) { - tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); - tags.putIfAbsent(Access.AccessTags.HGV.getKey(), Access.AccessTags.YES.getKey()); - } else if (Arrays.asList("motorway", "trunk", "motorway_link", "trunk_link").contains(highway)) { - tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); - tags.putIfAbsent(Access.AccessTags.BICYCLE.getKey(), Access.AccessTags.NO.getKey()); - tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.NO.getKey()); - } else if ("steps".equals(highway)) { - tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.NO.getKey()); - tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.YES.getKey()); - } else if ("path".equals(highway)) { - tags.putIfAbsent(Access.AccessTags.MOTOR_VEHICLE.getKey(), Access.AccessTags.NO.getKey()); - tags.putIfAbsent(Access.AccessTags.EMERGENCY.getKey(), Access.AccessTags.DESTINATION.getKey()); - } else if ("footway".equals(highway)) { - tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.DESIGNATED.getKey()); - } else if ("bus_guideway".equals(highway)) { - tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.NO.getKey()); - tags.putIfAbsent(Access.AccessTags.BUS.getKey(), Access.AccessTags.DESIGNATED.getKey()); - } else if ("road".equals(highway)) { // Don't expect these to be routable - tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.NO.getKey()); - } else { - tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.YES.getKey()); - } - return tags; - } - - /** - * Get the error level for a test - * - * @param test The integer value of the test error - * @return The severity for the test - */ - public static Severity getErrorLevel(int test) { - return SEVERITY_MAP.get(test); - } - - /** - * Set the error level for a test - * - * @param test The integer value of the test error - * @param severity The new severity for the test - */ - public static void setErrorLevel(int test, Severity severity) { - SEVERITY_MAP.put(test, severity); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/StreetAddressOrder.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/StreetAddressOrder.java deleted file mode 100644 index 6d4516a4..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/StreetAddressOrder.java +++ /dev/null @@ -1,297 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.validation.tests; - -import static org.openstreetmap.josm.tools.I18n.marktr; -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.data.osm.BBox; -import org.openstreetmap.josm.data.osm.IPrimitive; -import org.openstreetmap.josm.data.osm.IWay; -import org.openstreetmap.josm.data.osm.IWaySegment; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.RelationMember; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.data.validation.Severity; -import org.openstreetmap.josm.data.validation.Test; -import org.openstreetmap.josm.data.validation.TestError; -import org.openstreetmap.josm.data.validation.tests.SharpAngles; -import org.openstreetmap.josm.gui.progress.NullProgressMonitor; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.tools.Geometry; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Pair; - -/** - * Check the street address order - */ -public class StreetAddressOrder extends Test { - private static final SharpAngles ANGLES_TEST = new SharpAngles(); - - /** - * Create a new test object - */ - public StreetAddressOrder() { - super(tr("Address order ({0})", MapWithAIPlugin.NAME), tr("Check that street address order makes sense")); - } - - @Override - public void visit(Way way) { - if (way.isUsable() && way.hasTag("highway", StreetAddressTest.CLASSIFIED_HIGHWAYS) && way.hasTag("name")) { - String name = way.get("name"); - List addresses = getNearbyAddresses(way).stream().filter(Objects::nonNull) - .filter(w -> w.hasTag("addr:housenumber")).filter(w -> name.equals(w.get("addr:street"))) - .sorted(Comparator.comparing(p -> convertAddrHouseNumberToDouble(p.get("addr:housenumber")))) - .collect(Collectors.toList()); - List leftAddresses = getAddressesInDirection(true, addresses, way); - List rightAddresses = getAddressesInDirection(false, addresses, way); - Map> potentialBadAddresses = new HashMap<>(checkOrdering(leftAddresses)); - potentialBadAddresses.putAll(checkOrdering(rightAddresses)); - potentialBadAddresses.forEach(this::createError); - } - } - - /** - * Get nearby addresses to a way - * - * @param way The way to get nearby addresses from - * @return The primitives that have appropriate addr tags near to the way - */ - public static List getNearbyAddresses(Way way) { - BBox bbox = StreetAddressTest.expandBBox(new BBox(way.getBBox()), StreetAddressTest.BBOX_EXPANSION); - BBox competingHighwaysBbox = StreetAddressTest.expandBBox(new BBox(way.getBBox()), - 2 * StreetAddressTest.BBOX_EXPANSION); - List competingHighways = way.getDataSet().searchWays(competingHighwaysBbox); - List addrNodes = way.getDataSet().searchNodes(bbox).stream() - .filter(StreetAddressTest::hasStreetAddressTags).collect(Collectors.toList()); - List addrWays = way.getDataSet().searchWays(bbox).stream().filter(StreetAddressTest::hasStreetAddressTags) - .collect(Collectors.toList()); - List addrRelations = way.getDataSet().searchRelations(bbox).stream() - .filter(StreetAddressTest::hasStreetAddressTags).collect(Collectors.toList()); - ArrayList foundObjects = new ArrayList<>(addrNodes.size() + addrWays.size() + addrRelations.size()); - // This isn't as pretty as using a stream, but it significantly reduces the call - // stack size - for (Node node : addrNodes) { - if (isNearestRoad(way, competingHighways, node)) { - foundObjects.add(node); - } - } - for (Way addrWay : addrWays) { - if (isNearestRoad(way, competingHighways, addrWay)) { - foundObjects.add(addrWay); - } - } - for (Relation rel : addrRelations) { - if (isNearestRoad(way, competingHighways, rel)) { - foundObjects.add(rel); - } - } - foundObjects.trimToSize(); - return foundObjects; - } - - /** - * Check if a way is the nearest road to a primitive - * - * @param way The way to check - * @param prim The primitive to get the distance from - * @return {@code true} if the primitive is the nearest way - */ - static boolean isNearestRoad(Way way, Collection competingHighways, OsmPrimitive prim) { - BBox primBBox = StreetAddressTest.expandBBox(new BBox(prim.getBBox()), StreetAddressTest.BBOX_EXPANSION); - List> sorted = competingHighways.stream().filter(StreetAddressTest::isHighway) - .filter(w -> primBBox.intersects(w.getBBox())).map(iway -> { - Pair p = StreetAddressTest.distanceToWay(iway, prim); - p.b = Math.sqrt(p.b); - return p; - }).sorted(Comparator.comparingDouble(p -> p.b)).collect(Collectors.toList()); - - if (!sorted.isEmpty()) { - double minDistance = sorted.get(0).b; - List nearby = sorted.stream().filter(p -> p.b - minDistance < StreetAddressTest.BBOX_EXPANSION * 0.05) - .map(p -> p.a).collect(Collectors.toList()); - return nearby.contains(way); - } - return false; - } - - /** - * Convert a housenumber (addr:housenumber) to a double - * - * @param housenumber The housenumber to convert - * @return The double representation, or {@link Double#NaN} if not convertible. - */ - public static double convertAddrHouseNumberToDouble(String housenumber) { - String[] parts = housenumber.split(" ", -1); - double number = 0; - for (String part : parts) { - try { - if (part.contains("/")) { - String[] fractional = part.split("/", -1); - double tmp = Double.parseDouble(fractional[0]); - for (int i = 1; i < fractional.length; i++) { - tmp = tmp / Double.parseDouble(fractional[i]); - } - number += tmp; - } else { - number += Double.parseDouble(part); - } - } catch (NumberFormatException e) { - Logging.debug("{0} found a malformed number {1}", MapWithAIPlugin.NAME, part); - Logging.debug(e); - number = Double.NaN; - } - } - return number; - } - - /** - * Create the error to show the user - * - * @param potentialBadAddress The potential bad address - * @param surroundingAddresses The surrounding addresses that make the - * potentialBadAddress bad - */ - public void createError(IPrimitive potentialBadAddress, Collection surroundingAddresses) { - if (potentialBadAddress instanceof OsmPrimitive) { - errors.add(TestError.builder(this, Severity.OTHER, 58_542_100) - .primitives((OsmPrimitive) potentialBadAddress) - .highlight(surroundingAddresses.stream().filter(OsmPrimitive.class::isInstance) - .map(OsmPrimitive.class::cast).collect(Collectors.toSet())) - .message(tr("{0} (experimental)", MapWithAIPlugin.NAME), marktr("Potential bad address")).build()); - } - } - - /** - * Check the ordering of primitives by creating nodes at their centroids and - * checking to see if a sharp angle is created. - * - * @param The type of the primitive - * @param primitives The primitives to check the order of - * @return Primitives that are out of order - * @see SharpAngles - */ - public static Map> checkOrdering(List primitives) { - if (primitives.isEmpty()) { - return Collections.emptyMap(); - } - List centroids = primitives.stream().map(StreetAddressOrder::getCentroid).filter(Objects::nonNull) - .collect(Collectors.toList()); - - Way way = new Way(); - way.setNodes(centroids); - double maxDistance = 100; - Node previousCentroid = centroids.get(0); - for (Node centroid : centroids) { - if (previousCentroid.equals(centroid)) { - continue; - } - double tDistance = Geometry.getDistance(centroid, previousCentroid); - previousCentroid = centroid; - if (tDistance > maxDistance) { - maxDistance = tDistance; - } - } - way.put("highway", "residential"); // Required for the SharpAngles test - ANGLES_TEST.startTest(NullProgressMonitor.INSTANCE); - ANGLES_TEST.setMaxLength(maxDistance); - ANGLES_TEST.visit(way); - ANGLES_TEST.endTest(); - List issueCentroids = ANGLES_TEST.getErrors().stream().flatMap(e -> e.getHighlighted().stream()) - .filter(Node.class::isInstance).map(Node.class::cast).collect(Collectors.toList()); - ANGLES_TEST.clear(); - way.setNodes(Collections.emptyList()); - List badPrimitives = issueCentroids.stream().map(centroids::indexOf).map(primitives::get) - .collect(Collectors.toList()); - return badPrimitives.stream().map(p -> new Pair<>(p, getNeighbors(p, primitives))) - .collect(Collectors.toMap(p -> p.a, p -> p.b)); - } - - /** - * Get the neighbors of a primitive from a list - * - * @param The type of the primitive - * @param p The primitive to get neighbors for - * @param orderedNeighbors The ordered list of primitives of which p is part of - * @return The primitive before/after p - */ - public static List getNeighbors(T p, List orderedNeighbors) { - int index = orderedNeighbors.indexOf(p); - List neighbors = new ArrayList<>(); - if (index > 0) { - neighbors.add(orderedNeighbors.get(index - 1)); - } - if (index < orderedNeighbors.size() - 1) { - neighbors.add(orderedNeighbors.get(index + 1)); - } - return neighbors; - } - - /** - * Get addresses on different sides of the road - * - * @param The type of the primitive - * @param left If {@code true}, get addresses on the "left" side of the - * road - * @param addresses Addresses to filter for the side on the road - * @param way The road way - * @return Addresses on the appropriate side of the road - */ - public static List getAddressesInDirection(boolean left, Collection addresses, - IWay way) { - List addressesToReturn = new ArrayList<>(); - for (T address : addresses) { - if (address instanceof OsmPrimitive && way instanceof Way) { - Node centroid = getCentroid(address); - IWaySegment seg = Geometry.getClosestWaySegment((Way) way, (OsmPrimitive) address); - if (seg.getFirstNode().getEastNorth() != null && seg.getSecondNode().getEastNorth() != null - && centroid != null && centroid.getEastNorth() != null) { - boolean right = Geometry.angleIsClockwise(seg.getFirstNode(), seg.getSecondNode(), centroid); - if (left != right) { - addressesToReturn.add(address); - } - } - } - } - return addressesToReturn; - } - - /** - * Get the centroid of a primitive - * - * @param primitive The primitive to get a centroid for - * @return The node that represents the centroid, or {@code null} if no centroid - * can be determined - */ - public static Node getCentroid(IPrimitive primitive) { - if (primitive instanceof Node) { - return (Node) primitive; - } else if (primitive instanceof Way) { - return new Node(Geometry.getCentroid(((Way) primitive).getNodes())); - } else if (primitive instanceof Relation && "multipolygon".equals(primitive.get("type"))) { - // This is not perfect by any stretch of the imagination - List nodes = new ArrayList<>(); - for (RelationMember member : ((Relation) primitive).getMembers()) { - if (member.hasRole("outer")) { - nodes.add(getCentroid(member.getMember())); - } - } - if (!nodes.isEmpty()) { - return new Node(Geometry.getCentroid(nodes)); - } - } - return null; - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/StreetAddressTest.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/StreetAddressTest.java deleted file mode 100644 index 3a7c3cf1..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/StreetAddressTest.java +++ /dev/null @@ -1,324 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.validation.tests; - -import static org.openstreetmap.josm.tools.I18n.marktr; -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.openstreetmap.josm.data.coor.EastNorth; -import org.openstreetmap.josm.data.coor.ILatLon; -import org.openstreetmap.josm.data.osm.BBox; -import org.openstreetmap.josm.data.osm.IPrimitive; -import org.openstreetmap.josm.data.osm.IWay; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.data.projection.ProjectionRegistry; -import org.openstreetmap.josm.data.validation.OsmValidator; -import org.openstreetmap.josm.data.validation.Severity; -import org.openstreetmap.josm.data.validation.Test; -import org.openstreetmap.josm.data.validation.TestError; -import org.openstreetmap.josm.data.validation.util.ValUtil; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.tools.CheckParameterUtil; -import org.openstreetmap.josm.tools.Pair; - -/** - * Check for addr:street and street name mismatches - */ -public class StreetAddressTest extends Test { - /** Standard bbox expansion */ - public static final double BBOX_EXPANSION = 0.002; - private static final String ADDR_STREET = "addr:street"; - private final Set namePrimitiveMap = new HashSet<>(); - private final Map> nameMap = new HashMap<>(); - private final Map> primitiveCellMap = new HashMap<>(); - /** - * Classified highways. This uses a {@link Set} instead of a {@link List} since - * the MapWithAI code doesn't care about order. - *

- * Copied from {@link org.openstreetmap.josm.data.validation.tests.Highways} - */ - public static final Set CLASSIFIED_HIGHWAYS = Collections - .unmodifiableSet(new HashSet<>(Arrays.asList("motorway", "motorway_link", "trunk", "trunk_link", "primary", - "primary_link", "secondary", "secondary_link", "tertiary", "tertiary_link", "unclassified", - "residential", "living_street", "service", "road"))); - - /** - * Create a new test object - */ - public StreetAddressTest() { - super(tr("Mismatched street/street addresses ({0})", MapWithAIPlugin.NAME), - tr("Check for addr:street/street name mismatches")); - } - - @Override - public void visit(Relation relation) { - realVisit(relation); - } - - @Override - public void visit(Way way) { - realVisit(way); - } - - @Override - public void visit(Node node) { - if (node.isLatLonKnown()) { - realVisit(node); - } - } - - @Override - public void endTest() { - for (var entry : this.primitiveCellMap.entrySet()) { - var names = getSurroundingHighwayNames(entry.getKey()); - for (var primitive : entry.getValue()) { - if (!primitive.isOutsideDownloadArea()) { - if ((this.partialSelection || this.isBeforeUpload) && !names.contains(primitive.get(ADDR_STREET)) - && primitive.getDataSet() != null) { - final var bbox = new BBox(primitive.getBBox()); - bbox.addPrimitive(primitive, 0.01); - for (Way way : primitive.getDataSet().searchWays(bbox)) { - if (isHighway(way)) { - this.visit(way); - } - } - names = getSurroundingHighwayNames(entry.getKey()); - } - if (!names.contains(primitive.get(ADDR_STREET))) { - namePrimitiveMap.add(primitive); - } - if (this.isCanceled()) { - break; - } - } - } - if (this.isCanceled()) { - break; - } - } - - final var values = namePrimitiveMap.stream().collect(Collectors.groupingBy(p -> p.get(ADDR_STREET))); - values.forEach(this::createError); - namePrimitiveMap.clear(); - this.nameMap.clear(); - this.primitiveCellMap.clear(); - } - - /** - * Create the error - * - * @param addrStreet The addr:street tag - * @param primitives The bad primitives - */ - public void createError(String addrStreet, List primitives) { - errors.add(TestError.builder(this, Severity.WARNING, 2_136_232) - .message(tr("{0} (experimental)", MapWithAIPlugin.NAME), - marktr("Addresses are not nearby a matching road ({0})"), addrStreet) - .primitives(primitives).build()); - } - - private void realVisit(OsmPrimitive primitive) { - if (primitive.isUsable()) { - final double gridDetail = OsmValidator.getGridDetail() / 100; - if (isHighway(primitive)) { - Collection names = getWayNames(primitive); - if (names.isEmpty()) { - return; - } - List nodes = ((Way) primitive).getNodes(); - for (var i = 0; i < nodes.size() - 1; i++) { - // Populate the name map - final var n1 = nodes.get(i); - final var n2 = nodes.get(i + 1); - for (Point2D cell : ValUtil.getSegmentCells(n1, n2, gridDetail)) { - this.nameMap.computeIfAbsent(new Point(cell.getX(), cell.getY()), k -> new HashSet<>()) - .addAll(names); - } - } - } else if (hasStreetAddressTags(primitive) && !primitive.isOutsideDownloadArea()) { - final EastNorth en; - if (primitive instanceof Node node) { - en = node.getEastNorth(); - } else { - en = primitive.getBBox().getCenter().getEastNorth(ProjectionRegistry.getProjection()); - } - if (en != null) { - long x = (long) Math.floor(en.getX() * gridDetail); - long y = (long) Math.floor(en.getY() * gridDetail); - final var point = new Point(x, y); - primitiveCellMap.computeIfAbsent(point, p -> new ArrayList<>()).add(primitive); - } - } - } - } - - private static Collection getWayNames(IPrimitive way) { - return way.getInterestingTags().entrySet().stream() - .filter(e -> (e.getKey().contains("name") || e.getKey().contains("ref")) - && !e.getKey().contains("tiger")) - .map(Map.Entry::getValue).flatMap(s -> Stream.of(s.split(";", -1))).map(String::trim) - .filter(s -> !s.isEmpty()).collect(Collectors.toSet()); - } - - private Collection getSurroundingHighwayNames(Point point2D) { - if (this.nameMap.isEmpty()) { - return Collections.emptySet(); - } - final var surroundingWays = new HashSet(); - var surrounding = 2; - while (surroundingWays.isEmpty()) { - for (int x = -surrounding; x <= surrounding; x++) { - for (int y = -surrounding; y <= surrounding; y++) { - final var xPoint = (long) Math.floor(point2D.x() + x); - final var yPoint = (long) Math.floor(point2D.y() + y); - final var key = new Point(xPoint, yPoint); - if (this.nameMap.containsKey(key)) { - surroundingWays.addAll(this.nameMap.get(key)); - } - } - } - if (surroundingWays.isEmpty()) { - surrounding++; - } - } - return surroundingWays; - } - - /** - * Get the distance to a way - * - * @param way The way to get a distance to - * @param prim The primitive to get a distance from (LatLon space) - * @return A Pair<Way, Double> of the distance from the primitive to the - * way. - */ - static Pair distanceToWay(Way way, OsmPrimitive prim) { - final ILatLon[] nodes; - if (prim instanceof Node node) { - nodes = new ILatLon[] { node }; - } else if (prim instanceof Way way2) { - nodes = way2.getNodes().toArray(new ILatLon[0]); - } else if (prim instanceof Relation relation) { - nodes = relation.getMemberPrimitives().stream().filter(p -> p instanceof ILatLon || p instanceof Way) - .flatMap(p -> p instanceof ILatLon ? Stream.of((ILatLon) p) : ((Way) p).getNodes().stream()) - .toArray(ILatLon[]::new); - } else { - throw new IllegalArgumentException("Unknown primitive type: " + prim.getClass()); - } - double dist = Double.NaN; - final var wayNodes = way.getNodes(); - for (var i = 0; i < wayNodes.size() - 1; i++) { - final var a = wayNodes.get(i); - final var b = wayNodes.get(i + 1); - for (var node : nodes) { - double tDist = getSegmentNodeDistSq(a, b, node); - if (Double.isNaN(dist) || (!Double.isNaN(tDist) && tDist < dist)) { - dist = tDist; - } - } - } - return new Pair<>(way, dist); - } - - // ****** START COPY FROM GEOMETRY (EastNorth -> ILatLon, perf is more important - // than accuracy, some modifications for zero memalloc) ******* - /** - * Calculate closest distance between a line segment s1-s2 and a point p - * - * @param p1 start of segment - * @param p2 end of segment - * @param point the point - * @return the square of the euclidean distance from p to the closest point on - * the segment - */ - private static double getSegmentNodeDistSq(ILatLon p1, ILatLon p2, ILatLon point) { - CheckParameterUtil.ensureParameterNotNull(p1, "p1"); - CheckParameterUtil.ensureParameterNotNull(p2, "p2"); - CheckParameterUtil.ensureParameterNotNull(point, "point"); - - double ldx = p2.lon() - p1.lon(); - double ldy = p2.lat() - p1.lat(); - - // segment zero length - if (ldx == 0 && ldy == 0) - return p1.distanceSq(point); - - double pdx = point.lon() - p1.lon(); - double pdy = point.lat() - p1.lat(); - - double offset = (pdx * ldx + pdy * ldy) / (ldx * ldx + ldy * ldy); - - if (offset <= 0) - return p1.distanceSq(point); - else if (offset >= 1) - return p2.distanceSq(point); - // Math copied from ILatLon#interpolate to avoid memory allocation - return point.distanceSq((1 - offset) * p1.lon() + offset * p2.lon(), - (1 - offset) * p1.lat() + offset * p2.lat()); - } - // ****** END COPY FROM GEOMETRY ******** - - /** - * Check if the primitive has an appropriate highway tag - * - * @param prim The primitive to check - * @return {@code true} if it has a highway tag that is classified - */ - public static boolean isHighway(IPrimitive prim) { - return prim instanceof IWay && prim.hasTag("highway", CLASSIFIED_HIGHWAYS); - } - - /** - * Check if the primitive has appropriate address tags - * - * @param prim The primitive to check - * @return {@code true} if it has addr:street tags (may change) - */ - public static boolean hasStreetAddressTags(IPrimitive prim) { - return prim.hasTag(ADDR_STREET); - } - - /** - * Expand a bbox by a set amount - * - * @param bbox The bbox to expand - * @param degree The amount to expand the bbox by - * @return The bbox, for easy chaining - */ - public static BBox expandBBox(BBox bbox, double degree) { - bbox.add(bbox.getBottomRightLon() + degree, bbox.getBottomRightLat() - degree); - bbox.add(bbox.getTopLeftLon() - degree, bbox.getTopLeftLat() + degree); - return bbox; - } - - private record Point(double x, double y) implements Comparable { - - @Override - public int compareTo(Point other) { - if (other.x == this.x && other.y == this.y) { - return 0; - } - if (other.x < this.x) { - return -1; - } - if (other.x > this.x) { - return 1; - } - return Double.compare(other.y, this.y); - } -}} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/StubEndsTest.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/StubEndsTest.java deleted file mode 100644 index 0c543a64..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/validation/tests/StubEndsTest.java +++ /dev/null @@ -1,150 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.data.validation.tests; - -import static org.openstreetmap.josm.tools.I18n.marktr; -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.command.ChangeCommand; -import org.openstreetmap.josm.command.DeleteCommand; -import org.openstreetmap.josm.command.SequenceCommand; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.data.validation.Severity; -import org.openstreetmap.josm.data.validation.Test; -import org.openstreetmap.josm.data.validation.TestError; -import org.openstreetmap.josm.gui.progress.ProgressMonitor; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.spi.preferences.Config; - -/** - * Look for short ("stub") ends - */ -public class StubEndsTest extends Test { - private static final String HIGHWAY = "highway"; - private static final List BAD_HIGHWAYS = Arrays.asList("services", "rest_area"); - private static final double DEFAULT_MAX_LENGTH = 5.0; - private static final int ERROR_CODE = 333_300_239; - // Initialize for use with just a `visit` statement - private double maxLength = Config.getPref().getDouble(MapWithAIPlugin.NAME + ".stubendlength", DEFAULT_MAX_LENGTH); - - /** - * Create a new test object - */ - public StubEndsTest() { - super(tr("Stub Ends ({0})", MapWithAIPlugin.NAME), tr("Look for short ends on ways")); - } - - @Override - public void startTest(ProgressMonitor monitor) { - super.startTest(monitor); - // Ensure that we pick up changes made to the preference on a per-run basis - maxLength = Config.getPref().getDouble(MapWithAIPlugin.NAME + ".stubendlength", DEFAULT_MAX_LENGTH); - } - - @Override - public void visit(Way way) { - if (way.hasTag(HIGHWAY) && !BAD_HIGHWAYS.contains(way.get(HIGHWAY)) && !way.isClosed()) { - checkEnds(way); - } - } - - private void checkEnds(Way way) { - List nodesToFirstConnection = new ArrayList<>(); - double distanceToFirstConnection = distanceToFirstConnection(way, nodesToFirstConnection); - if (distanceToFirstConnection < maxLength && !nodesToFirstConnection.isEmpty()) { - errors.add(createError(way, nodesToFirstConnection, distanceToFirstConnection)); - } - - List nodesToLastConnection = new ArrayList<>(); - double distanceToLastConnection = distanceToLastConnection(way, nodesToLastConnection); - if (distanceToLastConnection < maxLength && !nodesToLastConnection.isEmpty()) { - errors.add(createError(way, nodesToLastConnection, distanceToLastConnection)); - } - } - - private TestError createError(Way way, List nodes, double distance) { - TestError.Builder error = TestError.builder(this, Severity.ERROR, ERROR_CODE) - .message(tr("{0} (experimental)", MapWithAIPlugin.NAME), marktr("Stub end ({0}m)"), - Math.round(distance)) - .primitives(way).highlight(nodes); - if (way.isNew()) { - Way tWay = new Way(way); - List tNodes = tWay.getNodes(); - boolean reversed = false; - if (Objects.equals(tWay.lastNode(), nodes.get(0))) { - reversed = true; - Collections.reverse(tNodes); - } - for (Node node : nodes) { - if (tNodes.get(0).equals(node)) { - tNodes.remove(0); - } - } - if (reversed) { - Collections.reverse(tNodes); - } - List nodesToDelete = nodes.stream().filter(node -> !node.hasKeys()) - .filter(node -> node.getReferrers().size() == 1).collect(Collectors.toList()); - tWay.setNodes(tNodes); - nodesToDelete.removeAll(tNodes); - error.fix(() -> new SequenceCommand(tr("Remove stub ends"), new ChangeCommand(way, tWay), - DeleteCommand.delete(nodesToDelete))); - } - return error.build(); - } - - private static double distanceToFirstConnection(Way way, List nodesToFirstConnection) { - return distanceToConnection(way, nodesToFirstConnection, way.getNodes()); - } - - private static double distanceToLastConnection(Way way, List nodesToLastConnection) { - List nodes = way.getNodes(); - Collections.reverse(nodes); - return distanceToConnection(way, nodesToLastConnection, nodes); - } - - private static double distanceToConnection(Way way, List nodesToConnection, List nodeOrder) { - double distance = 0; - Node previous = nodeOrder.get(0); - if (previous.hasTag("noexit")) { - return Double.NaN; - } - // isOutsideDownloadArea returns false if new or undeleted as well - if (!previous.isOutsideDownloadArea()) { - for (Node node : nodeOrder) { - List connectingWays = getConnectingWays(previous, way); - if (!node.equals(previous) && connectingWays.isEmpty()) { - nodesToConnection.add(previous); - if (node.isLatLonKnown() && previous.isLatLonKnown()) { - distance += node.greatCircleDistance(previous); - } - previous = node; - } - if (!connectingWays.isEmpty()) { - break; - } - } - } - return distance; - } - - /** - * Get the connecting ways for a node - * - * @param node The node to look for - * @param wayToIgnore The way to ignore, if any - * @return The connected ways - */ - private static List getConnectingWays(final Node node, final Way wayToIgnore) { - return node.referrers(Way.class).filter(tWay -> !tWay.equals(wayToIgnore)) - .filter(tWay -> tWay.hasTag(HIGHWAY) && !BAD_HIGHWAYS.contains(tWay.get(HIGHWAY))) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/MapWithAIMenu.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/MapWithAIMenu.java deleted file mode 100644 index a1f39689..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/MapWithAIMenu.java +++ /dev/null @@ -1,256 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui; - -import static org.openstreetmap.josm.tools.I18n.tr; -import static org.openstreetmap.josm.tools.I18n.trc; - -import javax.swing.Action; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.event.MenuEvent; -import javax.swing.event.MenuListener; - -import java.awt.Component; -import java.awt.GraphicsEnvironment; -import java.awt.MenuComponent; -import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.EnumMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import org.openstreetmap.josm.actions.JosmAction; -import org.openstreetmap.josm.data.coor.LatLon; -import org.openstreetmap.josm.data.imagery.Shape; -import org.openstreetmap.josm.data.sources.SourceInfo; -import org.openstreetmap.josm.gui.ImageryMenu; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.MenuScroller; -import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference; -import org.openstreetmap.josm.plugins.mapwithai.actions.AddMapWithAILayerAction; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAILayer; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.tools.ImageProvider; -import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; -import org.openstreetmap.josm.tools.ImageResource; -import org.openstreetmap.josm.tools.Logging; - -/** - * MapWithAI menu, holding entries for MapWithAI preferences and dynamic source - * entries depending on current mapview coordinates. - * - * Largely copied from {@link ImageryMenu}, but highly modified. - */ -public class MapWithAIMenu extends JMenu { - /** - * Compare MapWithAIInfo objects alphabetically by name. - * - * MapWithAIInfo objects are normally sorted by country code first (for the - * preferences). We don't want this in the MapWithAI menu. - */ - public static final Comparator> alphabeticSourceComparator = (ii1, ii2) -> ii1.getName() - .toLowerCase(Locale.ENGLISH).compareTo(ii2.getName().toLowerCase(Locale.ENGLISH)); - - /** - * Constructs a new {@code ImageryMenu}. - */ - public MapWithAIMenu() { - /* I18N: mnemonic: I */ - super(trc("menu", "MapWithAI")); - ImageProvider mapwithai = new ImageProvider("mapwithai").setOptional(true) - .setMaxSize(ImageProvider.ImageSizes.MENU); - ImageResource resource = mapwithai.getResource(); - if (resource != null) { - super.setIcon(resource.getImageIconBounded(ImageProvider.ImageSizes.MENU.getImageDimension())); - } - setupMenuScroller(); - // build dynamically - addMenuListener(new MenuListener() { - @Override - public void menuSelected(MenuEvent e) { - refreshImageryMenu(); - } - - @Override - public void menuDeselected(MenuEvent e) { - // Do nothing - } - - @Override - public void menuCanceled(MenuEvent e) { - // Do nothing - } - }); - } - - private void setupMenuScroller() { - if (!GraphicsEnvironment.isHeadless()) { - MenuScroller.setScrollerFor(this, 150, 2); - } - } - - /** - * For layers containing complex shapes, check that center is in one of its - * shapes (fix #7910) - * - * @param info layer info - * @param pos center - * @return {@code true} if center is in one of info shapes - */ - private static boolean isPosInOneShapeIfAny(SourceInfo info, LatLon pos) { - List shapes = info.getBounds().getShapes(); - return shapes == null || shapes.isEmpty() || shapes.stream().anyMatch(s -> s.contains(pos)); - } - - /** - * Refresh imagery menu. - * - * Outside this class only called in {@link ImageryPreference#initialize()}. (In - * order to have actions ready for the toolbar, see #8446.) - */ - public void refreshImageryMenu() { - removeDynamicItems(); - - addDynamicSeparator(); - // Get layers in use - MapWithAILayer layer = MapWithAIDataUtils.getLayer(false); - final Collection alreadyInUse = layer == null ? Collections.emptyList() - : layer.getDownloadedInfo(); - - // for each configured ImageryInfo, add a menu entry. - final List savedLayers = new ArrayList<>(MapWithAILayerInfo.getInstance().getLayers()); - savedLayers.sort(alphabeticSourceComparator); - savedLayers.removeIf(alreadyInUse::contains); - for (final MapWithAIInfo u : savedLayers) { - addDynamic(trackJosmAction(new AddMapWithAILayerAction(u)), null); - } - - // list all imagery entries where the current map location is within the imagery - // bounds - if (MainApplication.isDisplayingMapView()) { - final var mv = MainApplication.getMap().mapView; - final var pos = mv.getProjection().eastNorth2latlon(mv.getCenter()); - final var inViewLayers = MapWithAILayerInfo.getInstance().getAllDefaultLayers().stream() - .filter(i -> i.getBounds() != null && i.getBounds().contains(pos) && !alreadyInUse.contains(i) - && !savedLayers.contains(i) && isPosInOneShapeIfAny(i, pos)) - .sorted(alphabeticSourceComparator).toList(); - if (!inViewLayers.isEmpty()) { - if (inViewLayers.stream().anyMatch(i -> i.getCategory() == i.getCategory().getDefault())) { - addDynamicSeparator(); - } - for (MapWithAIInfo i : inViewLayers) { - addDynamic(trackJosmAction(new AddMapWithAILayerAction(i)), i.getCategory()); - } - } - if (!dynamicNonPhotoItems.isEmpty()) { - addDynamicSeparator(); - for (Map.Entry> e : dynamicNonPhotoItems.entrySet()) { - MapWithAICategory cat = e.getKey(); - List list = e.getValue(); - if (list.size() > 1) { - JMenuItem categoryMenu = new JMenu(cat.getDescription()); - categoryMenu.setIcon(cat.getIcon(ImageSizes.MENU)); - for (JMenuItem it : list) { - categoryMenu.add(it); - } - dynamicNonPhotoMenus.add(add(categoryMenu)); - } else if (!list.isEmpty()) { - dynamicNonPhotoMenus.add(add(list.get(0))); - } - } - } - if (dynJosmActions.isEmpty()) { - JosmAction infoAction = new JosmAction() { - @Override - public void actionPerformed(ActionEvent e) { - // Do nothing - } - }; - infoAction.putValue(Action.NAME, tr("No futher download options")); - infoAction.setEnabled(false); - infoAction.setTooltip(tr("No further download actions possible in this area")); - addDynamic(infoAction, null); - } - } - } - - /** - * List to store temporary "photo" menu items. They will be deleted (and - * possibly recreated) when refreshImageryMenu() is called. - */ - private final List dynamicItems = new ArrayList<>(20); - /** - * Map to store temporary "not photo" menu items. They will be deleted (and - * possibly recreated) when refreshImageryMenu() is called. - */ - private final Map> dynamicNonPhotoItems = new EnumMap<>(MapWithAICategory.class); - /** - * List to store temporary "not photo" submenus. They will be deleted (and - * possibly recreated) when refreshImageryMenu() is called. - */ - private final List dynamicNonPhotoMenus = new ArrayList<>(20); - private final List dynJosmActions = new ArrayList<>(20); - - /** - * Remove all the items in dynamic items collection - * - * @since 5803 - */ - private void removeDynamicItems() { - dynJosmActions.forEach(JosmAction::destroy); - dynJosmActions.clear(); - dynamicItems.forEach(this::removeDynamicItem); - dynamicItems.clear(); - dynamicNonPhotoMenus.forEach(this::removeDynamicItem); - dynamicItems.clear(); - dynamicNonPhotoItems.clear(); - } - - private void removeDynamicItem(Object item) { - if (item instanceof JMenuItem) { - remove((JMenuItem) item); - } else if (item instanceof MenuComponent) { - remove((MenuComponent) item); - } else if (item instanceof Component) { - remove((Component) item); - } else { - Logging.error("Unknown imagery menu item type: {0}", item); - } - } - - private void addDynamicSeparator() { - JPopupMenu.Separator s = new JPopupMenu.Separator(); - dynamicItems.add(s); - add(s); - } - - private void addDynamic(Action a, MapWithAICategory category) { - JMenuItem item = createActionComponent(a); - item.setAction(a); - doAddDynamic(item, category); - } - - private void doAddDynamic(JMenuItem item, MapWithAICategory category) { - if (category == null || category == MapWithAICategory.FEATURED) { - dynamicItems.add(this.add(item)); - } else { - dynamicNonPhotoItems.computeIfAbsent(category, x -> new ArrayList<>()).add(item); - } - } - - private Action trackJosmAction(Action action) { - if (action instanceof JosmAction) { - dynJosmActions.add((JosmAction) action); - } - return action; - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/download/MapWithAIDownloadOptions.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/download/MapWithAIDownloadOptions.java deleted file mode 100644 index 505f451a..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/download/MapWithAIDownloadOptions.java +++ /dev/null @@ -1,98 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui.download; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JPanel; - -import java.awt.Component; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.stream.Stream; - -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.gui.bbox.BBoxChooser; -import org.openstreetmap.josm.gui.download.DownloadDialog; -import org.openstreetmap.josm.gui.download.DownloadSelection; -import org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai.MapWithAIProvidersPanel; -import org.openstreetmap.josm.tools.Destroyable; -import org.openstreetmap.josm.tools.GBC; - -/** - * Add options for MapWithAI downloads to the main JOSM download window - * - * @author Taylor Smock - */ -public class MapWithAIDownloadOptions extends JPanel implements DownloadSelection, Destroyable, PropertyChangeListener { - private final JPanel optionPanel; - private DownloadDialog iGui; - private final MapWithAIProvidersPanel mapwithaiProvidersPanel; - - /** - * Create a new options panel - */ - public MapWithAIDownloadOptions() { - optionPanel = new JPanel(new GridBagLayout()); - final var infoHeader = new JPanel(); - infoHeader.add(new JLabel("Browse and activate extra data sets to facilitate your mapping needs.")); - optionPanel.add(infoHeader, GBC.eol().fill(GridBagConstraints.HORIZONTAL).anchor(GridBagConstraints.NORTH)); - mapwithaiProvidersPanel = new MapWithAIProvidersPanel(this); - optionPanel.add(mapwithaiProvidersPanel, - GBC.eol().fill(GridBagConstraints.BOTH).anchor(GridBagConstraints.CENTER)); - mapwithaiProvidersPanel.defaultMap.addPropertyChangeListener(this); - } - - @Override - public void addGui(DownloadDialog gui) { - iGui = gui; - iGui.addDownloadAreaSelector(optionPanel, tr("Browse MapWithAI Data Sources")); - iGui.addDownloadAreaListener(this); - } - - @Override - public void setDownloadArea(Bounds area) { - // This is (currently) never called. - // See https://josm.openstreetmap.de/ticket/19310 - mapwithaiProvidersPanel.setCurrentBounds(area); - } - - @Override - public void destroy() { - if (this.iGui != null) { - for (var component : getJComponents(this.iGui.getComponents())) { - removeFromComponent(component); - } - this.iGui.removeDownloadAreaListener(this); - this.iGui = null; - } - } - - private static JComponent[] getJComponents(Component[] components) { - return Stream.of(components).filter(JComponent.class::isInstance).map(JComponent.class::cast) - .toArray(JComponent[]::new); - } - - private boolean removeFromComponent(JComponent component) { - for (var newComponent : getJComponents(component.getComponents())) { - if (optionPanel.equals(newComponent)) { - component.remove(optionPanel); - return true; - } else if (removeFromComponent(newComponent)) { - return true; - } - } - return false; - } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(BBoxChooser.BBOX_PROP) && iGui != null) { - mapwithaiProvidersPanel.fireAreaListeners(); - iGui.boundingBoxChanged((Bounds) evt.getNewValue(), this); - } - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/download/MapWithAIDownloadSourceType.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/download/MapWithAIDownloadSourceType.java deleted file mode 100644 index f3f13a41..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/download/MapWithAIDownloadSourceType.java +++ /dev/null @@ -1,97 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui.download; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.Icon; -import javax.swing.JCheckBox; -import javax.swing.event.ChangeListener; - -import java.util.stream.Stream; - -import org.openstreetmap.josm.actions.downloadtasks.AbstractDownloadTask; -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.coor.ILatLon; -import org.openstreetmap.josm.data.coor.LatLon; -import org.openstreetmap.josm.data.preferences.BooleanProperty; -import org.openstreetmap.josm.gui.download.IDownloadSourceType; -import org.openstreetmap.josm.plugins.mapwithai.backend.DownloadMapWithAITask; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo.LayerChangeListener; -import org.openstreetmap.josm.tools.ImageProvider; - -/** - * Adds the MapWithAI download checkbox to the JOSM download UI - */ -public class MapWithAIDownloadSourceType implements IDownloadSourceType, LayerChangeListener { - static final BooleanProperty IS_ENABLED = new BooleanProperty("download.mapwithai.data", false); - JCheckBox cbDownloadMapWithAIData; - - @Override - public Icon getIcon() { - return ImageProvider.get("mapwithai", ImageProvider.ImageSizes.SMALLICON); - } - - @Override - public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) { - if (cbDownloadMapWithAIData == null) { - cbDownloadMapWithAIData = new JCheckBox(tr("MapWithAI data"), getBooleanProperty().get()); - cbDownloadMapWithAIData - .setToolTipText(tr("Select to download MapWithAI data in the selected download area.")); - cbDownloadMapWithAIData.getModel() - .addActionListener(l -> getBooleanProperty().put(cbDownloadMapWithAIData.isSelected())); - MapWithAILayerInfo.getInstance().addListener(this); - } - if (checkboxChangeListener != null) { - cbDownloadMapWithAIData.getModel().addChangeListener(checkboxChangeListener); - } - return cbDownloadMapWithAIData; - } - - @Override - public Class> getDownloadClass() { - return DownloadMapWithAITask.class; - } - - @Override - public BooleanProperty getBooleanProperty() { - return IS_ENABLED; - } - - @Override - public boolean isDownloadAreaTooLarge(Bounds bound) { - return isDownloadAreaTooLargeStatic(bound); - } - - /** - * Check if the download area is too large - * - * @param bound The bound to check - * @return {@code true} if the area is too large - */ - public static boolean isDownloadAreaTooLargeStatic(Bounds bound) { - double width = Math.max( - bound.getMin().greatCircleDistance((ILatLon) new LatLon(bound.getMinLat(), bound.getMaxLon())), - bound.getMax().greatCircleDistance((ILatLon) new LatLon(bound.getMaxLat(), bound.getMinLon()))); - double height = Math.max( - bound.getMin().greatCircleDistance((ILatLon) new LatLon(bound.getMaxLat(), bound.getMinLon())), - bound.getMax().greatCircleDistance((ILatLon) new LatLon(bound.getMinLat(), bound.getMaxLon()))); - return height > MapWithAIDataUtils.MAXIMUM_SIDE_DIMENSIONS - || width > MapWithAIDataUtils.MAXIMUM_SIDE_DIMENSIONS; - } - - @Override - public void changeEvent(MapWithAIInfo modified) { - if (Stream.of(Thread.currentThread().getStackTrace()).map(StackTraceElement::getClassName) - .noneMatch(p -> p.contains("PreferenceDialog"))) { - if (MapWithAILayerInfo.getInstance().getLayers().contains(modified)) { - this.cbDownloadMapWithAIData.setSelected(true); - } else if (MapWithAILayerInfo.getInstance().getLayers().isEmpty()) { - this.cbDownloadMapWithAIData.setSelected(false); - } - } - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/MapWithAIPreferences.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/MapWithAIPreferences.java deleted file mode 100644 index 022cf863..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/MapWithAIPreferences.java +++ /dev/null @@ -1,267 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui.preferences; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.Box; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSpinner; -import javax.swing.JTabbedPane; -import javax.swing.SpinnerNumberModel; - -import java.awt.Component; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.actions.ExpertToggleAction; -import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting; -import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; -import org.openstreetmap.josm.gui.preferences.advanced.PrefEntry; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIPreferenceHelper; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai.MapWithAIProvidersPanel; -import org.openstreetmap.josm.spi.preferences.StringSetting; -import org.openstreetmap.josm.tools.GBC; -import org.openstreetmap.josm.tools.ImageProvider; -import org.openstreetmap.josm.tools.OpenBrowser; - -/** - * The MapWithAI preference pane - */ -public class MapWithAIPreferences extends DefaultTabPreferenceSetting { - private final JCheckBox switchLayerCheckBox; - private final JCheckBox mergeBuildingAddressCheckBox; - private final JSpinner maximumAdditionSpinner; - private final ReplacementPreferenceTable replacementPreferenceTable; - private final List replacementTableDisplayData; - private static final int MAX_SELECTED_TO_EDIT = 1; - - /** - * Create the preference panel - */ - public MapWithAIPreferences() { - super("mapwithai", tr("MapWithAI preferences"), tr("Modify MapWithAI preferences"), false, new JTabbedPane()); - - switchLayerCheckBox = new JCheckBox(); - maximumAdditionSpinner = new JSpinner(new SpinnerNumberModel(MapWithAIPreferenceHelper.getMaximumAddition(), 0, - MapWithAIPreferenceHelper.getDefaultMaximumAddition() * 10, 1)); - mergeBuildingAddressCheckBox = new JCheckBox(); - replacementTableDisplayData = new ArrayList<>(); - fillReplacementTagDisplayData(replacementTableDisplayData); - replacementPreferenceTable = new ReplacementPreferenceTable(replacementTableDisplayData); - } - - private static void fillReplacementTagDisplayData(List list) { - MapWithAIPreferenceHelper.getReplacementTags().forEach( - (key, value) -> list.add(new PrefEntry(key, new StringSetting(value), new StringSetting(null), false))); - } - - @Override - public void addGui(PreferenceTabbedPane gui) { - final var p = gui.createPreferenceTab(this); - final var panel = getTabPane(); - if (panel.getTabCount() == 0) { - panel.addTab(tr("Servers"), getServerList(gui)); - panel.addTab(tr("Settings"), getSettingsPanel(gui)); - } - p.add(panel, GBC.std().fill(GridBagConstraints.BOTH)); - } - - private static Component getServerList(PreferenceTabbedPane gui) { - return new MapWithAIProvidersPanel(gui, MapWithAIProvidersPanel.Options.SHOW_ACTIVE); - } - - private Component getSettingsPanel(PreferenceTabbedPane gui) { - final var pane = new JPanel(new GridBagLayout()); - final var width = 200; - final var height = 200; - final var switchLayer = new JLabel(tr("Automatically switch layers")); - final var maximumAddition = new JLabel(tr("Maximum features (add)")); - final var mergeBuildingWithAddress = new JLabel(tr("Merge address nodes and buildings")); - - switchLayer.setToolTipText( - tr("If checked, automatically switch from the {0} layer to the OSM layer when objects are added", - MapWithAIPlugin.NAME)); - maximumAddition.setToolTipText( - tr("This is the maximum number of complete OSM objects that can be added from the {0} layer, " - + "child objects do not count to this limit", MapWithAIPlugin.NAME)); - mergeBuildingWithAddress - .setToolTipText(tr("If checked, automatically merge address nodes onto added buildings, " - + "if and only if one address is within the building boundary")); - - switchLayerCheckBox.setSelected(MapWithAIPreferenceHelper.isSwitchLayers()); - switchLayerCheckBox.setToolTipText(switchLayer.getToolTipText()); - mergeBuildingAddressCheckBox.setSelected(MapWithAIPreferenceHelper.isMergeBuildingAddress()); - mergeBuildingAddressCheckBox.setToolTipText(mergeBuildingWithAddress.getToolTipText()); - - pane.setAlignmentY(Component.TOP_ALIGNMENT); - pane.setAlignmentX(Component.LEFT_ALIGNMENT); - - final var first = GBC.std().weight(0, 1).anchor(GridBagConstraints.WEST); - final var second = GBC.eol().fill(GridBagConstraints.HORIZONTAL); - final var buttonInsets = GBC.std().insets(5, 5, 0, 0); - - pane.add(switchLayer, first); - - pane.add(switchLayerCheckBox, second); - switchLayerCheckBox.setToolTipText(switchLayer.getToolTipText()); - - pane.add(maximumAddition, first); - pane.add(maximumAdditionSpinner, second); - maximumAdditionSpinner.setToolTipText(maximumAddition.getToolTipText()); - - pane.add(mergeBuildingWithAddress, first); - pane.add(mergeBuildingAddressCheckBox, second); - - final var expertHorizontalGlue = Box.createHorizontalGlue(); - pane.add(expertHorizontalGlue, GBC.eol().fill(GridBagConstraints.HORIZONTAL)); - final var previewFeatureSets = new JLabel(tr("Show Preview DataSets")); - final var previewFeatureSetCheckbox = new JCheckBox(); - final var previewFeatureSetProperty = MapWithAILayerInfo.SHOW_PREVIEW; - previewFeatureSetCheckbox.setToolTipText(tr("If selected, show datasets which may have various issues")); - previewFeatureSetCheckbox.setSelected(Boolean.TRUE.equals(previewFeatureSetProperty.get())); - previewFeatureSetCheckbox - .addChangeListener(l -> previewFeatureSetProperty.put(previewFeatureSetCheckbox.isSelected())); - pane.add(previewFeatureSets, first); - pane.add(previewFeatureSetCheckbox, second); - - final var replacementTags = new JLabel(tr("Replacement Tags (to be replaced on download)")); - pane.add(replacementTags, first); - final var scroll2 = new JScrollPane(replacementPreferenceTable); - pane.add(scroll2, GBC.eol().fill(GridBagConstraints.BOTH)); - scroll2.setPreferredSize(new Dimension(width, height)); - - pane.add(new JLabel(), first); - final var replaceAddEditDeleteScroll2 = new JPanel(new GridBagLayout()); - pane.add(replaceAddEditDeleteScroll2, second); - final var addScroll2 = new JButton(tr("Add")); - replaceAddEditDeleteScroll2.add(addScroll2, buttonInsets); - addScroll2.addActionListener(e -> { - final var pe = replacementPreferenceTable.addPreference(gui); - if ((pe != null) && (pe.getValue() instanceof StringSetting)) { - replacementTableDisplayData.add(pe); - Collections.sort(replacementTableDisplayData); - replacementPreferenceTable.fireDataChanged(); - } - }); - - final var editScroll2 = new JButton(tr("Edit")); - replaceAddEditDeleteScroll2.add(editScroll2, buttonInsets); - editScroll2.addActionListener(e -> { - final var toEdit = replacementPreferenceTable.getSelectedItems(); - if (toEdit.size() == MAX_SELECTED_TO_EDIT) { - replacementPreferenceTable.editPreference(gui); - } - }); - - final var deleteScroll2 = new JButton(tr("Delete")); - replaceAddEditDeleteScroll2.add(deleteScroll2, buttonInsets); - deleteScroll2.addActionListener(e -> { - final var toRemove = replacementPreferenceTable.getSelectedItems(); - if (!toRemove.isEmpty()) { - replacementTableDisplayData.removeAll(toRemove); - } - replacementPreferenceTable.fireDataChanged(); - }); - - pane.add(Box.createHorizontalGlue(), second); - - final var kaartLogo = new JButton(ImageProvider.getIfAvailable("kaart") == null ? null - : new ImageProvider("kaart").setHeight(ImageProvider.ImageSizes.SETTINGS_TAB.getAdjustedHeight()) - .get()); - kaartLogo.setToolTipText(tr("Link to source code repository")); - kaartLogo.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - OpenBrowser.displayUrl("https://github.com/JOSM/MapWithAI"); - } - }); - kaartLogo.setCursor(new Cursor(Cursor.HAND_CURSOR)); - pane.add(kaartLogo, GBC.std().anchor(GridBagConstraints.WEST)); - - final var mapWithAILogo = new JButton(ImageProvider.getIfAvailable("mapwithai_text") == null ? null - : new ImageProvider("mapwithai_text") - .setHeight(ImageProvider.ImageSizes.SETTINGS_TAB.getAdjustedHeight()).get()); - mapWithAILogo.setCursor(new Cursor(Cursor.HAND_CURSOR)); - mapWithAILogo.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - OpenBrowser.displayUrl("https://mapwith.ai"); - } - }); - pane.add(mapWithAILogo, GBC.eol().anchor(GridBagConstraints.EAST)); - - Arrays.asList(replaceAddEditDeleteScroll2, scroll2, expertHorizontalGlue, replacementTags, previewFeatureSets, - previewFeatureSetCheckbox).forEach(ExpertToggleAction::addVisibilitySwitcher); - return pane; - } - - @Override - public boolean ok() { - MapWithAIPreferenceHelper.setSwitchLayers(switchLayerCheckBox.isSelected(), true); - final var value = maximumAdditionSpinner.getValue(); - MapWithAIPreferenceHelper.setMergeBuildingAddress(this.mergeBuildingAddressCheckBox.isSelected(), true); - if (value instanceof Number number) { - MapWithAIPreferenceHelper.setMaximumAddition(number.intValue(), true); - } - MapWithAILayerInfo.getInstance().save(); - MapWithAILayerInfo.getInstance().clear(); - MapWithAILayerInfo.getInstance().load(false, null); - MapWithAIPreferenceHelper.setReplacementTags(convertReplacementPrefToMap(replacementTableDisplayData)); - return false; - } - - private static Map convertReplacementPrefToMap(List displayData) { - return displayData.stream() - .collect(Collectors.toMap(PrefEntry::getKey, entry -> entry.getValue().getValue().toString())); - } - - @Override - public boolean isExpert() { - return false; - } - - /** - * This method returns the checkbox used for deciding if layers should be - * switched. - * - * @return The {@code JCheckBox} for whether or not we are switching layers. - */ - public JCheckBox getSwitchLayerCheckBox() { - return switchLayerCheckBox; - } - - /** - * Get the checkbox for merging buildings/addresses - * - * @return The checkbox - */ - public JCheckBox getMergeBuildingAddressCheckBox() { - return mergeBuildingAddressCheckBox; - } - - /** - * This is the spinner used to determine maximum additions (and maximum - * selections). - * - * @return {@code JSpinner} for the maximum additions - */ - public JSpinner getMaximumAdditionSpinner() { - return maximumAdditionSpinner; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/ReplacementPreferenceTable.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/ReplacementPreferenceTable.java deleted file mode 100644 index 062bbe2f..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/ReplacementPreferenceTable.java +++ /dev/null @@ -1,63 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui.preferences; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.util.List; - -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSeparator; - -import org.openstreetmap.josm.gui.ExtendedDialog; -import org.openstreetmap.josm.gui.preferences.advanced.PrefEntry; -import org.openstreetmap.josm.gui.preferences.advanced.PreferencesTable; -import org.openstreetmap.josm.gui.widgets.JosmTextField; -import org.openstreetmap.josm.spi.preferences.StringSetting; -import org.openstreetmap.josm.tools.GBC; - -/** - * @author Taylor Smock - */ -class ReplacementPreferenceTable extends PreferencesTable { - /** - * @param displayData The initial preference entries to display - */ - public ReplacementPreferenceTable(List displayData) { - super(displayData); - } - - @Override - public PrefEntry addPreference(final JComponent gui) { - final JPanel p = new JPanel(new GridBagLayout()); - p.add(new JLabel(tr("Original Tag")), GBC.std().insets(0, 0, 5, 0)); - final JosmTextField tkey = new JosmTextField("", 50); - p.add(tkey, GBC.eop().insets(5, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL)); - p.add(new JLabel(tr("Replacement Tag")), GBC.std().insets(0, 0, 5, 0)); - final JosmTextField tValue = new JosmTextField("", 50); - p.add(tValue, GBC.eop().insets(5, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL)); - - p.add(new JSeparator(), GBC.eop().insets(5, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL)); - p.add(new JLabel(tr("Example")), GBC.eop().insets(5, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL)); - p.add(new JLabel(tr("Original Tag")), GBC.std().insets(0, 0, 5, 0)); - JosmTextField tmp = new JosmTextField("highway=residential"); - tmp.setEditable(false); - p.add(tmp, GBC.eop().insets(5, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL)); - p.add(new JLabel(tr("Replacement Tag")), GBC.std().insets(0, 0, 5, 0)); - tmp = new JosmTextField("disused:highway=road"); - tmp.setEditable(false); - p.add(tmp, GBC.eop().insets(5, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL)); - - return askAddSetting(gui, p) - ? new PrefEntry(tkey.getText(), new StringSetting(tValue.getText()), new StringSetting(null), false) - : null; - } - - protected static boolean askAddSetting(JComponent gui, JPanel p) { - return new ExtendedDialog(gui, tr("Add setting"), tr("OK"), tr("Cancel")).setContent(p) - .setButtonIcons("ok", "cancel").showDialog().getValue() == 1; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/AddMapWithAIDialog.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/AddMapWithAIDialog.java deleted file mode 100644 index 994aa09b..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/AddMapWithAIDialog.java +++ /dev/null @@ -1,50 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.awt.Component; -import java.awt.Dimension; - -import org.openstreetmap.josm.gui.ExtendedDialog; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.preferences.imagery.AddImageryPanel.ContentValidationListener; -import org.openstreetmap.josm.gui.util.WindowGeometry; - -/** - * Dialog shown to add a new source from preferences. - * - */ -class AddMapWithAIDialog extends ExtendedDialog implements ContentValidationListener { - private static final long serialVersionUID = 7513676077181970148L; - - /** - * Constructs a new AddMapWithAIDialog. - * - * @param parent The parent element that will be used for position and maximum - * size - * @param panel The content that will be displayed in the message dialog - */ - public AddMapWithAIDialog(Component parent, AddMapWithAIPanel panel) { - super(parent, tr("Add MapWithAI URL"), tr("OK"), tr("Cancel")); - setButtonIcons("ok", "cancel"); - setCancelButton(2); - configureContextsensitiveHelp("/Preferences/MapWithAI", true /* show help button */); - setContent(panel, false); - setMinimumSize(new Dimension(300, 400)); - panel.addContentValidationListener(this); - setRememberWindowGeometry(panel.getClass().getName() + ".geometry", - WindowGeometry.centerInWindow(MainApplication.getMainFrame(), new Dimension(400, 600))); - } - - @Override - public void setupDialog() { - super.setupDialog(); - contentChanged(false); - } - - @Override - public void contentChanged(boolean isValid) { - buttons.get(0).setEnabled(isValid); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/AddMapWithAIPanel.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/AddMapWithAIPanel.java deleted file mode 100644 index 94d0b02c..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/AddMapWithAIPanel.java +++ /dev/null @@ -1,262 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.AbstractButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.SpinnerNumberModel; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.text.JTextComponent; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.LayoutManager; -import java.awt.event.ItemEvent; -import java.io.Serial; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import org.openstreetmap.josm.actions.ExpertToggleAction; -import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; -import org.openstreetmap.josm.data.imagery.TMSCachedTileLoaderJob; -import org.openstreetmap.josm.gui.preferences.imagery.AddImageryPanel.ContentValidationListener; -import org.openstreetmap.josm.gui.preferences.imagery.HeadersTable; -import org.openstreetmap.josm.gui.widgets.JosmTextArea; -import org.openstreetmap.josm.gui.widgets.JosmTextField; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType; -import org.openstreetmap.josm.tools.GBC; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Pair; - -import jakarta.json.Json; -import jakarta.json.JsonArray; -import jakarta.json.JsonArrayBuilder; -import jakarta.json.JsonObjectBuilder; - -/** - * A panel used to add MapWithAI sources. - */ -class AddMapWithAIPanel extends JPanel { - @Serial - private static final long serialVersionUID = -2838267045934203122L; - private final transient JPanel layerPanel = new JPanel(new GridBagLayout()); - - protected final JosmTextArea rawUrl = new JosmTextArea(3, 40).transferFocusOnTab(); - protected final JosmTextField name = new JosmTextField(); - - protected final transient Collection listeners = new ArrayList<>(); - - private final JCheckBox validGeoreference = new JCheckBox(tr("Is layer properly georeferenced?")); - private HeadersTable headersTable; - private MapWithAIParametersPanel parametersTable; - private JSpinner minimumCacheExpiry; - private JComboBox minimumCacheExpiryUnit; - private TimeUnit currentUnit; - private MapWithAIType type; - - private transient MapWithAIInfo info; - private JComboBox typeBox; - - protected AddMapWithAIPanel(LayoutManager layout) { - super(layout); - registerValidableComponent(name); - } - - /** - * default constructor - */ - public AddMapWithAIPanel() { - this(new GridBagLayout()); - headersTable = new HeadersTable(); - parametersTable = new MapWithAIParametersPanel(); - minimumCacheExpiry = new JSpinner( - new SpinnerNumberModel(TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get()), - 0L, Integer.MAX_VALUE, 1)); - List units = Arrays.asList(tr("seconds"), tr("minutes"), tr("hours"), tr("days")); - minimumCacheExpiryUnit = new JComboBox<>(units.toArray(new String[] {})); - currentUnit = TimeUnit.SECONDS; - minimumCacheExpiryUnit.addItemListener(e -> { - if (e.getStateChange() == ItemEvent.SELECTED) { - long newValue = 0; - switch (units.indexOf(e.getItem())) { - case 0 -> { - newValue = currentUnit.toSeconds((long) minimumCacheExpiry.getValue()); - currentUnit = TimeUnit.SECONDS; - } - case 1 -> { - newValue = currentUnit.toMinutes((long) minimumCacheExpiry.getValue()); - currentUnit = TimeUnit.MINUTES; - } - case 2 -> { - newValue = currentUnit.toHours((long) minimumCacheExpiry.getValue()); - currentUnit = TimeUnit.HOURS; - } - case 3 -> { - newValue = currentUnit.toDays((long) minimumCacheExpiry.getValue()); - currentUnit = TimeUnit.DAYS; - } - default -> Logging.warn("Unknown unit: " + units.indexOf(e.getItem())); - } - minimumCacheExpiry.setValue(newValue); - } - }); - add(new JLabel(tr("{0} Make sure OSM has the permission to use this service", "1.")), GBC.eol()); - add(new JLabel(tr("{0} Enter Service URL", "2.")), GBC.eol()); - add(rawUrl, GBC.eop().fill(GridBagConstraints.HORIZONTAL)); - rawUrl.setLineWrap(true); - rawUrl.setAlignmentY(TOP_ALIGNMENT); - add(layerPanel, GBC.eol().fill()); - - addCommonSettings(); - - add(new JLabel(tr("{0} Enter name for this source", "3.")), GBC.eol()); - add(name, GBC.eol().fill(GridBagConstraints.HORIZONTAL)); - add(new JLabel(tr("{0} What is the type of this source?", "4.")), GBC.eol()); - typeBox = new JComboBox<>(MapWithAIType.values()); - typeBox.setSelectedItem(MapWithAIType.THIRD_PARTY); - typeBox.addItemListener(l -> { - type = (MapWithAIType) typeBox.getSelectedItem(); - notifyListeners(); - }); - add(typeBox, GBC.eol()); - registerValidableComponent(rawUrl); - } - - public AddMapWithAIPanel(MapWithAIInfo info) { - this(); - this.info = info; - rawUrl.setText(info.getUrl()); - name.setText(info.getName()); - typeBox.setSelectedItem(info.getSourceType()); - this.info.setSourceType(this.type); - if (info.getParameters() != null) { - parametersTable.setParameters(info.getParameters()); - } - parametersTable.addListener(e -> notifyListeners()); - } - - protected void addCommonSettings() { - add(new JLabel(tr("Set parameters")), GBC.eop()); - add(parametersTable, GBC.eol().fill()); - if (ExpertToggleAction.isExpert()) { - add(new JLabel(tr("Minimum cache expiry: "))); - add(minimumCacheExpiry); - add(minimumCacheExpiryUnit, GBC.eol()); - add(new JLabel(tr("Set custom HTTP headers (if needed):")), GBC.eop()); - add(headersTable, GBC.eol().fill()); - add(validGeoreference, GBC.eop().fill(GridBagConstraints.HORIZONTAL)); - } - } - - protected Map getCommonHeaders() { - return headersTable.getHeaders(); - } - - protected Map> getCommonParameters() { - return parametersTable.getParameters(); - } - - protected boolean getCommonIsValidGeoreference() { - return validGeoreference.isSelected(); - } - - protected final void registerValidableComponent(AbstractButton component) { - component.addChangeListener(e -> notifyListeners()); - } - - protected final void registerValidableComponent(JTextComponent component) { - component.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void removeUpdate(DocumentEvent e) { - notifyListeners(); - } - - @Override - public void insertUpdate(DocumentEvent e) { - notifyListeners(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - notifyListeners(); - } - }); - } - - private static JsonArray convertToJsonParameterArray(Map> parameters) { - JsonArrayBuilder builder = Json.createArrayBuilder(); - for (Map.Entry> entry : parameters.entrySet()) { - JsonObjectBuilder entryBuilder = Json.createObjectBuilder(); - entryBuilder.add("description", entry.getKey()); - entryBuilder.add("parameter", entry.getValue().a); - entryBuilder.add("enabled", entry.getValue().b); - builder.add(entryBuilder.build()); - } - return builder.build(); - } - - protected MapWithAIInfo getSourceInfo() { - MapWithAIInfo ret = info == null ? new MapWithAIInfo() : info; - ret.setName(getImageryName()); - ret.setUrl(getImageryRawUrl()); - ret.setCustomHttpHeaders(getCommonHeaders()); - ret.setSourceType(this.type); - ret.setParameters(convertToJsonParameterArray(getCommonParameters())); - return ret; - } - - protected static String sanitize(String s) { - return s.replaceAll("[\r\n]+", "").trim(); - } - - protected static String sanitize(String s, ImageryType type) { - String ret = s; - String imageryType = type.getTypeString() + ':'; - if (ret.startsWith(imageryType)) { - // remove ImageryType from URL - ret = ret.substring(imageryType.length()); - } - return sanitize(ret); - } - - protected final String getImageryName() { - return sanitize(name.getText()); - } - - protected final String getImageryRawUrl() { - return sanitize(rawUrl.getText()); - } - - protected boolean isSourceValid() { - return !getImageryName().isEmpty() && !getImageryRawUrl().isEmpty(); - } - - /** - * Registers a new ContentValidationListener - * - * @param l The new ContentValidationListener that will be notified of - * validation status changes - */ - public final void addContentValidationListener(ContentValidationListener l) { - if (l != null) { - listeners.add(l); - } - } - - private void notifyListeners() { - for (ContentValidationListener l : listeners) { - l.contentChanged(isSourceValid()); - } - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIDefaultLayerTableModel.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIDefaultLayerTableModel.java deleted file mode 100644 index 47dfba0e..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIDefaultLayerTableModel.java +++ /dev/null @@ -1,97 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import javax.swing.table.DefaultTableModel; - -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; - -/** - * The table model for the default imagery layer list - */ -class MapWithAIDefaultLayerTableModel extends DefaultTableModel { - private static final long serialVersionUID = -2966437364160797385L; - private final List> columnTypes; - private final transient List> columnDataRetrieval; - - /** - * Constructs a new {@code MapWithAIDefaultLayerTableModel}. - */ - public MapWithAIDefaultLayerTableModel() { - setColumnIdentifiers(new String[] { "", tr("MapWithAI Data Source Name (Default)"), tr("Type"), - tr("MapWithAI URL (Default)"), tr("Provider"), tr("License"), tr("Enabled") }); - columnTypes = Stream - .of(MapWithAICategory.class, MapWithAIInfo.class, List.class, String.class, String.class, Boolean.class) - .collect(Collectors.toCollection(ArrayList::new)); - columnDataRetrieval = new ArrayList<>(); - columnDataRetrieval.add(info -> Optional.ofNullable(info.getCategory()).orElse(MapWithAICategory.OTHER)); - columnDataRetrieval.add(info -> info); - columnDataRetrieval.add(info -> { - List categories = Stream - .concat(Stream.of(info.getCategory()), info.getAdditionalCategories().stream()) - .filter(Objects::nonNull).map(MapWithAICategory::getDescription).collect(Collectors.toList()); - return categories.isEmpty() ? Collections.singletonList(MapWithAICategory.OTHER.getDescription()) - : categories; - }); - columnDataRetrieval.add(MapWithAIInfo::getUrl); - columnDataRetrieval.add(i -> i.getAttributionText(0, null, null)); - columnDataRetrieval.add(info -> Optional.ofNullable(info.getTermsOfUseURL()).orElse("")); - columnDataRetrieval.add(i -> MapWithAILayerInfo.getInstance().getLayers().contains(i)); - MapWithAILayerInfo.getInstance().addFinishListener(() -> GuiHelper.runInEDT(this::fireTableDataChanged)); - MapWithAILayerInfo.SHOW_PREVIEW.addWeakListener(l -> GuiHelper.runInEDT(this::fireTableDataChanged)); - } - - /** - * Returns the imagery info at the given row number. - * - * @param row The row number - * @return The imagery info at the given row number - */ - public static MapWithAIInfo getRow(int row) { - final var layers = MapWithAILayerInfo.getInstance().getAllDefaultLayers(); - if (row == 0 && layers.isEmpty()) { - return new MapWithAIInfo(tr("Loading"), ""); - } - return layers.get(row); - } - - @Override - public int getRowCount() { - return Math.max(MapWithAILayerInfo.getInstance().getAllDefaultLayers().size(), 1); - } - - @Override - public Class getColumnClass(int columnIndex) { - if (columnIndex < columnTypes.size()) { - return columnTypes.get(columnIndex); - } - return super.getColumnClass(columnIndex); - } - - @Override - public Object getValueAt(int row, int column) { - MapWithAIInfo info = getRow(row); - if (column < columnDataRetrieval.size()) { - return columnDataRetrieval.get(column).apply(info); - } - return null; - } - - @Override - public boolean isCellEditable(int row, int column) { - return false; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAILayerTableModel.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAILayerTableModel.java deleted file mode 100644 index 8c26e7d8..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAILayerTableModel.java +++ /dev/null @@ -1,123 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.table.DefaultTableModel; - -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; - -/** - * The table model for source layer list - */ -class MapWithAILayerTableModel extends DefaultTableModel { - private static final long serialVersionUID = 60378230494588007L; - - /** - * Constructs a new {@code MapWithAILayerTableModel}. - */ - public MapWithAILayerTableModel() { - setColumnIdentifiers(new String[] { tr("Default Source Value"), tr("MapWithAI URL") }); - } - - /** - * Returns the source info at the given row number. - * - * @param row The row number - * @return The source info at the given row number - */ - public static MapWithAIInfo getRow(int row) { - return MapWithAILayerInfo.getInstance().getLayers().get(row); - } - - /** - * Adds a new imagery info as the last row. - * - * @param i The imagery info to add - */ - public void addRow(MapWithAIInfo i) { - MapWithAILayerInfo.getInstance().add(i); - int p = getRowCount() - 1; - fireTableRowsInserted(p, p); - } - - @Override - public void removeRow(int i) { - MapWithAILayerInfo.getInstance().remove(getRow(i)); - fireTableRowsDeleted(i, i); - } - - @Override - public int getRowCount() { - return MapWithAILayerInfo.getInstance().getLayers().size(); - } - - @Override - public Object getValueAt(int row, int column) { - MapWithAIInfo info = MapWithAILayerInfo.getInstance().getLayers().get(row); - switch (column) { - case 0: - return info.getName(); - case 1: - return info.getUrl(); - default: - throw new ArrayIndexOutOfBoundsException(Integer.toString(column)); - } - } - - @Override - public void setValueAt(Object o, int row, int column) { - if (MapWithAILayerInfo.getInstance().getLayers().size() <= row) { - return; - } - MapWithAIInfo info = MapWithAILayerInfo.getInstance().getLayers().get(row); - switch (column) { - case 0: - info.setName((String) o); - info.clearId(); - break; - case 1: - info.setUrl((String) o); - info.clearId(); - break; - default: - throw new ArrayIndexOutOfBoundsException(Integer.toString(column)); - } - } - - /** - * Check if the active table contains the MapWithAIInfo object - * - * @param info The info to check - * @return {@code true} if any of the active layers is functionally equal - */ - public static boolean contains(MapWithAIInfo info) { - return MapWithAILayerInfo.getInstance().getLayers().stream().anyMatch(info::equalsBaseValues); - } - - /** - * Check if the active table does not contain the MapWithAIInfo object - * - * @param info The info to check - * @return {@code true} if none of the active layers is functionally equal - */ - public static boolean doesNotContain(MapWithAIInfo info) { - return !contains(info); - } - - /** - * Get the index that a specified MapWithAIInfo resides at - * - * @param info The MapWithAIInfo to find - * @return The row index, or -1 if it isn't in the model - */ - public int getRowIndex(MapWithAIInfo info) { - for (int j = 0; j < getRowCount(); j++) { - if (info.equalsBaseValues(getRow(j))) { - return j; - } - } - return -1; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIParametersPanel.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIParametersPanel.java deleted file mode 100644 index fc416b27..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIParametersPanel.java +++ /dev/null @@ -1,193 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.tr; - -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; -import javax.swing.event.TableModelListener; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableModel; - -import java.awt.GridBagLayout; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.tools.GBC; -import org.openstreetmap.josm.tools.Pair; - -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; - -/** - * Parameters panel for adding MapWithAI URLs - * - * @author Taylor Smock - * - */ -class MapWithAIParametersPanel extends JPanel { - - private final class ParametersTableModel extends AbstractTableModel { - private final Set disabledRows = new HashSet<>(); - - @Override - public String getColumnName(int column) { - return switch (column) { - case 0 -> tr("Parameter name"); - case 1 -> tr("Parameter value"); - case 2 -> tr("Enabled"); - default -> ""; - }; - } - - @Override - public Class getColumnClass(int column) { - if (column == 2) { - return Boolean.class; - } - return String.class; - } - - @Override - public int getRowCount() { - return headers.size() + 1; - } - - @Override - public int getColumnCount() { - return 3; - } - - @Override - public Object getValueAt(int row, int col) { - if (row < headers.size()) { - return headers.get(row)[col]; - } - if (String.class.equals(getColumnClass(col))) { - return ""; - } - return null; - } - - @Override - public boolean isCellEditable(int row, int column) { - return !disabledRows.contains(row); - } - - /** - * Prevent a row from being edited - * - * @param row The row that shouldn't be editable - * @return See {@link Set#add} - */ - public boolean disableRowEdits(int row) { - return disabledRows.add(row); - } - - @Override - public void setValueAt(Object value, int row, int col) { - if (row < headers.size()) { - Object[] headerRow = headers.get(row); - headerRow[col] = value; - if ("".equals(headerRow[0]) && "".equals(headerRow[1])) { - headers.remove(row); - fireTableRowsDeleted(row, row); - } - - } else if (row == headers.size()) { - Object[] entry = { "", "", null }; - entry[col] = value; - headers.add(entry); - fireTableRowsInserted(row + 1, row + 1); - } - fireTableCellUpdated(row, col); - } - } - - private final List headers; - private final ParametersTableModel model; - - /** - * Creates empty table - */ - public MapWithAIParametersPanel() { - this(new ConcurrentHashMap<>()); - } - - /** - * Create table prefilled with headers - * - * @param headers contents of table - */ - public MapWithAIParametersPanel(Map> headers) { - super(new GridBagLayout()); - this.headers = getHeadersAsVector(headers); - this.model = new ParametersTableModel(); - final var table = new JTable(model); - table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - table.setAutoCreateRowSorter(true); - table.setRowSelectionAllowed(false); - table.setColumnSelectionAllowed(false); - add(new JScrollPane(table), GBC.eol().fill()); - } - - private static List getHeadersAsVector(Map> headers) { - return headers.entrySet().stream().sorted(Map.Entry.comparingByKey()) - .map(e -> new Object[] { e.getKey(), e.getValue().a, e.getValue().b }).collect(Collectors.toList()); - } - - /** - * HTTP parameters (so {@code ?param1=value1¶m2=value2}) - * - * @return parameters provided by user - */ - public Map> getParameters() { - return headers.stream().distinct() - .collect(Collectors.toMap(x -> (String) x[0], x -> new Pair<>((String) x[1], (Boolean) x[2]))); - } - - /** - * These are the current parameters for the info object. - * - * @param parameters The initial parameters to show in the dialog - */ - public void setParameters(JsonArray parameters) { - var i = 0; - for (JsonObject obj : parameters.stream().filter(JsonObject.class::isInstance).map(JsonObject.class::cast) - .toList()) { - model.setValueAt(obj.getString("parameter"), i, 1); - model.setValueAt(obj.getString("description", ""), i, 0); - model.setValueAt(obj.getBoolean("enabled", false), i, 2); - final var permanent = obj.getBoolean("permanent", false); - if (permanent) { - model.disableRowEdits(i); - } - i++; - } - model.fireTableDataChanged(); - } - - /** - * Add a listener to the model ({@code model.addTableModelListener}) - * - * @param l A TableModelListener for the backing model - */ - public void addListener(TableModelListener l) { - model.addTableModelListener(l); - } - - /** - * Get the model that is displayed in the panel. - * - * @return The table model used to display parameters - */ - public TableModel getModel() { - return model; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java deleted file mode 100644 index 7eef587f..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java +++ /dev/null @@ -1,885 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai; - -import static org.openstreetmap.josm.tools.I18n.marktr; -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.event.ActionEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.IOException; -import java.io.Serial; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import javax.swing.AbstractAction; -import javax.swing.Box; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.JToolBar; -import javax.swing.SwingConstants; -import javax.swing.UIManager; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.DefaultTableCellRenderer; - -import org.openstreetmap.gui.jmapviewer.Coordinate; -import org.openstreetmap.gui.jmapviewer.MapPolygonImpl; -import org.openstreetmap.gui.jmapviewer.MapRectangleImpl; -import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon; -import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle; -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.coor.LatLon; -import org.openstreetmap.josm.data.preferences.NamedColorProperty; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.bbox.JosmMapViewer; -import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser; -import org.openstreetmap.josm.gui.preferences.imagery.ImageryProvidersPanel; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.gui.widgets.FilterField; -import org.openstreetmap.josm.gui.widgets.HtmlPanel; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo.LayerChangeListener; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType; -import org.openstreetmap.josm.plugins.mapwithai.io.mapwithai.ESRISourceReader; -import org.openstreetmap.josm.tools.GBC; -import org.openstreetmap.josm.tools.ImageProvider; -import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; -import org.openstreetmap.josm.tools.ImageResource; -import org.openstreetmap.josm.tools.ListenerList; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.OpenBrowser; - -import jakarta.annotation.Nonnull; - -/** - * A panel displaying imagery providers. Largely duplicates - * {@link ImageryProvidersPanel}. - * - * @since 15115 (extracted from ImageryPreferences) - */ -public class MapWithAIProvidersPanel extends JPanel { - - /** - * The options for the panel - */ - public enum Options { - /** Hide the active table */ - SHOW_ACTIVE - } - - @Serial - private static final long serialVersionUID = -5876039771496409422L; - // Public JTables and JosmMapViewer - /** The table of active providers **/ - public final JTable activeTable; - /** The table of default providers **/ - public final JTable defaultTable; - /** The filter of default providers **/ - private final FilterField defaultFilter; - /** The directory for dialog images */ - private static final String DIALOG_IMAGES_DIR = "dialogs"; - /** - * The selection listener synchronizing map display with table of default - * providers - **/ - private final transient DefListSelectionListener defaultTableListener; - /** The map displaying imagery bounds of selected default providers **/ - public final SlippyMapBBoxChooser defaultMap; - - // Public models - /** The model of active providers **/ - static final MapWithAILayerTableModel ACTIVE_MODEL = new MapWithAILayerTableModel(); - /** The model of default providers **/ - static final MapWithAIDefaultLayerTableModel DEFAULT_MODEL = new MapWithAIDefaultLayerTableModel(); - - // Public JToolbars - /** The toolbar on the right of active providers **/ - public final JToolBar activeToolbar; - /** The toolbar on the middle of the panel **/ - public final JToolBar middleToolbar; - /** The toolbar on the right of default providers **/ - public final JToolBar defaultToolbar; - - // Private members - private final JComponent gui; - private final transient ListenerList areaListeners = ListenerList.create(); - /** Options that were passed to the constructor */ - private final Options[] options; - - /** - * Listen for area updates - */ - protected interface AreaListener { - - /** - * Update the area - * - * @param area The bounds to update - */ - void updateArea(Bounds area); - } - - /** - * class to render an information of MapWithAI source - * - * @param type of information - */ - private static class MapWithAITableCellRenderer extends DefaultTableCellRenderer implements AreaListener { - - private static final NamedColorProperty IMAGERY_BACKGROUND_COLOR = new NamedColorProperty( - marktr("MapWithAI Background: Default"), new Color(200, 255, 200)); - private static final NamedColorProperty MAPWITHAI_AREA_BACKGROUND_COLOR = new NamedColorProperty( - marktr("MapWithAI Background: Layer in area"), Color.decode("#f1ffc7")); - - private final transient Function mapper; - private final transient Function tooltip; - private final transient BiConsumer decorator; - private final transient Function reverseMapper; - - private final boolean highlightIfActive; - - private transient Bounds area; - - /** - * Initialize a cell renderer with specific rules - * - * @param mapper Map from <T> to an Object (to get a cell - * renderer) - * @param reverseMapper Map from an Object to <T> - * @param tooltip The tooltip to show - * @param decorator The decorator - * @param highlightIfActive If true, highlight when the entry is activated - */ - MapWithAITableCellRenderer(Function mapper, Function reverseMapper, - Function tooltip, BiConsumer decorator, boolean highlightIfActive) { - this.mapper = mapper; - this.reverseMapper = reverseMapper; - this.tooltip = tooltip; - this.decorator = decorator; - this.highlightIfActive = highlightIfActive; - } - - @Override - @SuppressWarnings("unchecked") - public final Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, - boolean hasFocus, int row, int column) { - T obj = (T) value; - final var label = (JLabel) super.getTableCellRendererComponent(table, mapper.apply(obj), isSelected, - hasFocus, row, column); - final var defaultColor = UIManager.getColor("Table.background"); - final var selectedColor = UIManager.getColor("Table.selectionBackground"); - GuiHelper.setBackgroundReadable(label, defaultColor); - - GuiHelper.setBackgroundReadable(label, isSelected ? selectedColor : defaultColor); - if (this.highlightIfActive && obj != null) { - final var info = obj instanceof MapWithAIInfo ? (MapWithAIInfo) obj : reverseMapper.apply(obj); - if (info == null) { - GuiHelper.setBackgroundReadable(label, defaultColor); - } else { - if (MapWithAILayerTableModel.contains(info)) { - final var t = IMAGERY_BACKGROUND_COLOR.get(); - GuiHelper.setBackgroundReadable(label, isSelected ? t.darker() : t); - } else if (this.area != null && info.getBounds() != null - && (this.area.intersects(info.getBounds()) || info.getBounds().intersects(this.area))) { - final var t = MAPWITHAI_AREA_BACKGROUND_COLOR.get(); - GuiHelper.setBackgroundReadable(label, isSelected ? t.darker() : t); - } else { - GuiHelper.setBackgroundReadable(label, isSelected ? selectedColor : defaultColor); - } - } - - } - if (obj != null) { - label.setToolTipText(tooltip.apply(obj)); - if (decorator != null) { - decorator.accept(obj, label); - } - } - return label; - } - - @Override - public void updateArea(Bounds area) { - this.area = area; - } - } - - /** - * class to render the license/terms of use URL of MapWithAI source - */ - private static class MapWithAILicenseTableCellRenderer extends MapWithAITableCellRenderer { - - MapWithAILicenseTableCellRenderer() { - super(s -> s != null && !s.isEmpty() ? "License" : "", - u -> MapWithAILayerInfo.getInstance().getAllDefaultLayers().stream() - .filter(i -> u.equals(i.getUrl())).findFirst().orElse(null), - u -> u, null, true); - } - } - - /** - * class to render the URL information of MapWithAI source - * - * @since 8065 - */ - private static class MapWithAIURLTableCellRenderer extends MapWithAITableCellRenderer { - - MapWithAIURLTableCellRenderer() { - super(s -> s, u -> MapWithAILayerInfo.getInstance().getAllDefaultLayers().stream() - .filter(i -> u.equals(i.getUrl())).findFirst().orElse(null), u -> u, null, true); - } - } - - /** - * class to render the category information of MapWithAI source - */ - private static class MapWithAICategoryTableCellRenderer - extends MapWithAIProvidersPanel.MapWithAITableCellRenderer { - - MapWithAICategoryTableCellRenderer() { - super(cat -> null, i -> null, cat -> tr("MapWithAI category: {0}", cat.getDescription()), - (cat, label) -> label.setIcon(cat.getIcon(ImageSizes.TABLE)), false); - } - } - - /** - * class to render the country information of MapWithAI source - */ - private static class MapWithAITypeTableCellRenderer - extends MapWithAIProvidersPanel.MapWithAITableCellRenderer> { - - MapWithAITypeTableCellRenderer() { - super(MapWithAITypeTableCellRenderer::joinList, i -> null, MapWithAITypeTableCellRenderer::joinList, null, - false); - } - - private static String joinList(List list) { - return list != null ? String.join(",", list) : ""; - } - } - - /** - * class to render the source provider information of a MapWithAI source - */ - private static class MapWithAIProviderTableCellRenderer - extends MapWithAIProvidersPanel.MapWithAITableCellRenderer { - - MapWithAIProviderTableCellRenderer() { - super(s -> s, s -> null, s -> s, null, false); - } - - } - - /** - * class to render the name information of Imagery source - */ - private static class MapWithAINameTableCellRenderer - extends MapWithAIProvidersPanel.MapWithAITableCellRenderer { - - @Serial - private static final long serialVersionUID = 6669934435517244629L; - - MapWithAINameTableCellRenderer(boolean showActive) { - super(info -> info == null ? null : info.getName(), i -> null, MapWithAIInfo::getToolTipText, null, - showActive); - } - } - - private static class ProvidersTable extends JTable { - @Serial - private static final long serialVersionUID = -6136421378119093719L; - - ProvidersTable() { - super(ACTIVE_MODEL); - } - - @Override - public String getToolTipText(@Nonnull MouseEvent e) { - final var p = e.getPoint(); - try { - return ACTIVE_MODEL.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString(); - } catch (ArrayIndexOutOfBoundsException ex) { - Logging.debug(ex); - return null; - } - } - } - - /** - * Constructs a new {@code MapWithAIProvidersPanel}. - * - * @param gui The parent preference tab pane - * @param options The options for this instance - */ - public MapWithAIProvidersPanel(final JComponent gui, Options... options) { - super(new GridBagLayout()); - this.gui = gui; - this.options = options; - boolean showActive = Arrays.asList(options).contains(Options.SHOW_ACTIVE); - - activeTable = new ProvidersTable(); - activeTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); - - defaultTable = new JTable(DEFAULT_MODEL); - defaultTable.setAutoCreateRowSorter(true); - defaultFilter = new FilterField().filter(defaultTable, DEFAULT_MODEL); - - DEFAULT_MODEL.addTableModelListener(e -> activeTable.repaint()); - ACTIVE_MODEL.addTableModelListener(e -> defaultTable.repaint()); - - setupDefaultTable(defaultTable, options, areaListeners); - - final var mod = activeTable.getColumnModel(); - mod.getColumn(1).setPreferredWidth(800); - final var activeTableCellRenderer = new MapWithAIURLTableCellRenderer(); - areaListeners.addListener(activeTableCellRenderer); - mod.getColumn(1).setCellRenderer(activeTableCellRenderer); - mod.getColumn(0).setMaxWidth(200); - - final var remove = new RemoveEntryAction(); - activeTable.getSelectionModel().addListSelectionListener(remove); - - final var edit = new EditEntryAction(); - activeTable.getSelectionModel().addListSelectionListener(edit); - - add(new JLabel(tr("Available default entries:")), GBC.std().insets(5, 5, 0, 0)); - add(new JLabel(tr("Boundaries of selected MapWithAI entries:")), GBC.eol().insets(5, 5, 0, 0)); - - // Add default item list - final var defaultPane = new JPanel(new GridBagLayout()); - final var scrolldef = new JScrollPane(defaultTable); - scrolldef.setPreferredSize(new Dimension(200, 200)); - defaultPane.add(defaultFilter, GBC.eol().insets(0, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL)); - defaultPane.add(scrolldef, GBC.eol().insets(0, 0, 0, 0).fill(GridBagConstraints.BOTH)); - add(defaultPane, GBC.std().fill(GridBagConstraints.BOTH).weight(1.0, 0.6).insets(5, 0, 0, 0)); - - // Add default item map - defaultMap = new SlippyMapBBoxChooser(); - defaultMap.setTileSource(JosmMapViewer.DefaultOsmTileSourceProvider.get()); // for attribution - defaultMap.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON1) { - defaultMap.getAttribution().handleAttribution(e.getPoint(), true); - } - } - }); - defaultMap.setZoomControlsVisible(false); - defaultMap.setMinimumSize(new Dimension(100, 200)); - defaultMap.addJMVListener(e -> { - final var visibleRect = defaultMap.getVisibleRect(); - final var max = defaultMap.getPosition((int) visibleRect.getMaxX(), (int) visibleRect.getMaxY()); - final var min = defaultMap.getPosition((int) visibleRect.getMinX(), (int) visibleRect.getMinY()); - final var b = new Bounds( - new LatLon(Math.min(max.getLat(), min.getLat()), - LatLon.toIntervalLon(Math.min(max.getLon(), min.getLon()))), - new LatLon(Math.max(max.getLat(), min.getLat()), - LatLon.toIntervalLon(Math.max(max.getLon(), min.getLon())))); - - this.areaListeners.fireEvent(f -> f.updateArea(b)); - // This is required to ensure that all cells are appropriately coloured - // Both revalidate and repaint are needed. - this.defaultTable.revalidate(); - this.defaultTable.repaint(); - }); - add(defaultMap, GBC.std().fill(GridBagConstraints.BOTH).weight(0.33, 0.6).insets(5, 0, 0, 0)); - - defaultTableListener = new DefListSelectionListener(); - defaultTable.getSelectionModel().addListSelectionListener(defaultTableListener); - - defaultToolbar = new JToolBar(SwingConstants.VERTICAL); - defaultToolbar.setFloatable(false); - defaultToolbar.setBorderPainted(false); - defaultToolbar.setOpaque(false); - defaultToolbar.add(new ReloadAction()); - add(defaultToolbar, GBC.eol().anchor(GridBagConstraints.SOUTH).insets(0, 0, 5, 0)); - - final var help = new HtmlPanel( - tr("New default entries can be added in the GitHub Repository.", - "https://github.com/JOSM/MapWithAI/blob/pages/json/sources.json")); - help.enableClickableHyperlinks(); - add(help, GBC.eol().insets(10, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL)); - - final var activate = new ActivateAction(); - defaultTable.getSelectionModel().addListSelectionListener(activate); - final var btnActivate = new JButton(activate); - - middleToolbar = new JToolBar(SwingConstants.HORIZONTAL); - middleToolbar.setFloatable(false); - middleToolbar.setBorderPainted(false); - middleToolbar.setOpaque(false); - middleToolbar.add(btnActivate); - add(middleToolbar, GBC.eol().anchor(GridBagConstraints.CENTER).insets(5, 5, 5, 0)); - - add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL)); - - final var scroll = new JScrollPane(activeTable); - scroll.setPreferredSize(new Dimension(200, 200)); - - activeToolbar = new JToolBar(SwingConstants.VERTICAL); - activeToolbar.setFloatable(false); - activeToolbar.setBorderPainted(false); - activeToolbar.setOpaque(false); - activeToolbar.add(new NewEntryAction(MapWithAIType.THIRD_PARTY)); - activeToolbar.add(edit); - activeToolbar.add(remove); - if (showActive) { - add(new JLabel(tr("Selected entries:")), GBC.eol().insets(5, 0, 0, 0)); - add(scroll, GBC.std().fill(GridBagConstraints.BOTH).span(GridBagConstraints.RELATIVE).weight(1.0, 0.4) - .insets(5, 0, 0, 5)); - add(activeToolbar, GBC.eol().anchor(GridBagConstraints.NORTH).insets(0, 0, 5, 5)); - } - } - - private static void setupDefaultTable(JTable defaultTable, Options[] options, - ListenerList areaListeners) { - boolean showActive = Arrays.asList(options).contains(Options.SHOW_ACTIVE); - int tenXWidth = defaultTable.getFontMetrics(defaultTable.getFont()).stringWidth("XXXXXXXXXX"); - final var mod = defaultTable.getColumnModel(); - int urlWidth = (showActive ? 3 : 0) * tenXWidth; - mod.getColumn(6).setCellRenderer(defaultTable.getDefaultRenderer(Boolean.class)); - mod.getColumn(6).setMaxWidth(20); - mod.getColumn(5).setMaxWidth((!showActive ? 1 : 0) * tenXWidth); - mod.getColumn(5).setCellRenderer(new MapWithAILicenseTableCellRenderer()); - mod.getColumn(4).setPreferredWidth((showActive ? 2 : 0) * tenXWidth); - mod.getColumn(4).setCellRenderer(new MapWithAIProviderTableCellRenderer()); - mod.getColumn(3).setPreferredWidth(urlWidth); - final var defaultUrlTableCellRenderer = new MapWithAIURLTableCellRenderer(); - mod.getColumn(3).setCellRenderer(defaultUrlTableCellRenderer); - mod.getColumn(2).setPreferredWidth((int) ((showActive ? 0 : 0.3) * tenXWidth)); - - mod.getColumn(2).setCellRenderer(new MapWithAITypeTableCellRenderer()); - - final var defaultNameTableCellRenderer = new MapWithAINameTableCellRenderer(!showActive); - mod.getColumn(1).setCellRenderer(defaultNameTableCellRenderer); - mod.getColumn(1).setPreferredWidth((showActive ? 3 : 2) * tenXWidth); - mod.getColumn(0).setCellRenderer(new MapWithAICategoryTableCellRenderer()); - mod.getColumn(0).setMaxWidth(ImageProvider.ImageSizes.MENU.getAdjustedWidth() + 5); - - if (showActive) { - defaultTable.removeColumn(mod.getColumn(6)); - defaultTable.removeColumn(mod.getColumn(4)); - defaultTable.removeColumn(mod.getColumn(2)); - areaListeners.addListener(defaultUrlTableCellRenderer); - } else { - defaultTable.removeColumn(mod.getColumn(3)); - areaListeners.addListener(defaultNameTableCellRenderer); - } - defaultTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - clickListener(e); - } - }); - } - - /** - * This is a function to be called when a cell is clicked - * - * @param e The MouseEvent (used to get the appropriate JTable) - */ - private static void clickListener(MouseEvent e) { - if (e.getSource()instanceof JTable table && table.getSelectedRow() >= 0 && table.getSelectedColumn() >= 0) { - final int realCol = table.convertColumnIndexToModel(table.getSelectedColumn()); - final int realRow = table.convertRowIndexToModel(table.getSelectedRow()); - final var tableName = table.getModel().getColumnName(realCol); - if (tr("License").equals(tableName)) { - final var info = MapWithAIDefaultLayerTableModel.getRow(realRow); - if (info.getTermsOfUseURL() != null) { - OpenBrowser.displayUrl(info.getTermsOfUseURL()); - } - } else if (tr("Enabled").equals(tableName)) { - final var info = MapWithAIDefaultLayerTableModel.getRow(realRow); - final var instance = MapWithAILayerInfo.getInstance(); - if (instance.getLayers().contains(info)) { - instance.remove(info); - } else { - instance.add(info); - } - } - } - } - - /** - * Set the current bounds of the map and the area to select - * - * @param area The current area to highlight data from - */ - public void setCurrentBounds(Bounds area) { - this.defaultMap.setBoundingBox(area); - fireAreaListeners(); - } - - /** - * Fire area listeners - */ - public void fireAreaListeners() { - this.areaListeners.fireEvent(f -> f.updateArea(this.defaultMap.getBoundingBox())); - } - - // Listener of default providers list selection - private final class DefListSelectionListener implements ListSelectionListener { - - // The current drawn rectangles and polygons - private final Map mapRectangles; - private final Map> mapPolygons; - - private DefListSelectionListener() { - this.mapRectangles = new HashMap<>(); - this.mapPolygons = new HashMap<>(); - } - - private void clearMap() { - defaultMap.removeAllMapRectangles(); - defaultMap.removeAllMapPolygons(); - mapRectangles.clear(); - mapPolygons.clear(); - } - - @Override - public void valueChanged(ListSelectionEvent e) { - // First index can be set to -1 when the list is refreshed, so discard all map - // rectangles and polygons - if (e.getFirstIndex() == -1) { - clearMap(); - } else if (!e.getValueIsAdjusting()) { - // Only process complete (final) selection events - for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) { - if (i < defaultTable.getRowCount()) { - updateBoundsAndShapes(defaultTable.convertRowIndexToModel(i)); - } - } - // Cleanup residual selected bounds which may have disappeared after a filter - cleanupResidualBounds(); - // If needed, adjust map to show all map rectangles and polygons - if (!mapRectangles.isEmpty() || !mapPolygons.isEmpty()) { - defaultMap.setDisplayToFitMapElements(false, true, true); - defaultMap.zoomOut(); - } - } - } - - /** - * update bounds and shapes for a new entry - * - * @param i model index - */ - private void updateBoundsAndShapes(int i) { - final var bounds = MapWithAIDefaultLayerTableModel.getRow(i).getBounds(); - if (bounds != null) { - int viewIndex = defaultTable.convertRowIndexToView(i); - final var shapes = bounds.getShapes(); - if (shapes != null && !shapes.isEmpty()) { - if (defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) { - mapPolygons.computeIfAbsent(i, key -> { - final var list = new ArrayList(shapes.size()); - // Add new map polygons - for (var shape : shapes) { - final var polygon = new MapPolygonImpl(shape.getPoints()); - list.add(polygon); - defaultMap.addMapPolygon(polygon); - } - return list; - }); - } else if (mapPolygons.containsKey(i)) { - // Remove previously drawn map polygons - for (var polygon : mapPolygons.get(i)) { - defaultMap.removeMapPolygon(polygon); - } - mapPolygons.remove(i); - } - // Only display bounds when no polygons (shapes) are defined for this provider - } else { - if (defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) { - mapRectangles.computeIfAbsent(i, key -> { - // Add new map rectangle - final var topLeft = new Coordinate(bounds.getMaxLat(), bounds.getMinLon()); - final var bottomRight = new Coordinate(bounds.getMinLat(), bounds.getMaxLon()); - final var rectangle = new MapRectangleImpl(topLeft, bottomRight); - defaultMap.addMapRectangle(rectangle); - return rectangle; - }); - } else if (mapRectangles.containsKey(i)) { - // Remove previously drawn map rectangle - defaultMap.removeMapRectangle(mapRectangles.get(i)); - mapRectangles.remove(i); - } - } - } - } - - private void doCleanupResidualBounds(Map map, Consumer removalEffect) { - final var toRemove = new ArrayList(); - for (var i : map.keySet()) { - int viewIndex = defaultTable.convertRowIndexToView(i); - if (!defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) { - toRemove.add(i); - } - } - toRemove.forEach(i -> removalEffect.accept(map.remove(i))); - } - - private void cleanupResidualBounds() { - doCleanupResidualBounds(mapPolygons, l -> l.forEach(defaultMap::removeMapPolygon)); - doCleanupResidualBounds(mapRectangles, defaultMap::removeMapRectangle); - } - } - - private class NewEntryAction extends AbstractAction { - - @Serial - private static final long serialVersionUID = 7451336680150337942L; - - NewEntryAction(MapWithAIType type) { - putValue(NAME, type.toString()); - putValue(SHORT_DESCRIPTION, tr("Add a new {0} entry by entering the URL", type.toString())); - new ImageProvider(DIALOG_IMAGES_DIR, "add").getResource().attachImageIcon(this, true); - } - - @Override - public void actionPerformed(ActionEvent evt) { - final var p = new AddMapWithAIPanel(); - final var addDialog = new AddMapWithAIDialog(gui, p); - addDialog.showDialog(); - - if (addDialog.getValue() == 1) { - try { - final var info = p.getSourceInfo(); - // Fix a possible NPE - if (info.getSourceType() == null) { - info.setSourceType(MapWithAIType.THIRD_PARTY); - } - if (MapWithAIType.ESRI == info.getSourceType()) { - final var reader = new ESRISourceReader(info); - try { - for (var i : reader.parse()) { - try { - ACTIVE_MODEL.addRow(i.get()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - Logging.error(e); - } catch (ExecutionException e) { - Logging.error(e); - } - } - } catch (IOException e) { - Logging.error(e); - } - } else { - ACTIVE_MODEL.addRow(info); - } - } catch (IllegalArgumentException ex) { - if (ex.getMessage() == null || ex.getMessage().isEmpty()) { - throw ex; - } - JOptionPane.showMessageDialog(MainApplication.getMainFrame(), ex.getMessage(), tr("Error"), - JOptionPane.ERROR_MESSAGE); - } - } - } - } - - private class EditEntryAction extends AbstractAction implements ListSelectionListener { - - @Serial - private static final long serialVersionUID = -1682304557691078801L; - - /** - * Constructs a new {@code EditEntryAction}. - */ - EditEntryAction() { - putValue(NAME, tr("Edit")); - putValue(SHORT_DESCRIPTION, tr("Edit entry")); - new ImageProvider(DIALOG_IMAGES_DIR, "edit").getResource().attachImageIcon(this, true); - updateEnabledState(); - } - - protected final void updateEnabledState() { - setEnabled(activeTable.getSelectedRowCount() > 0); - } - - @Override - public void valueChanged(ListSelectionEvent e) { - updateEnabledState(); - } - - @Override - public void actionPerformed(ActionEvent e) { - if (activeTable.getSelectedRow() != -1) { - final var p = new AddMapWithAIPanel(MapWithAILayerTableModel.getRow(activeTable.getSelectedRow())); - final var addDialog = new AddMapWithAIDialog(gui, p); - addDialog.showDialog(); - if (addDialog.getValue() == 1) { - p.getSourceInfo(); - } - } - } - } - - private class RemoveEntryAction extends AbstractAction implements ListSelectionListener { - - @Serial - private static final long serialVersionUID = 2666450386256004180L; - - /** - * Constructs a new {@code RemoveEntryAction}. - */ - RemoveEntryAction() { - putValue(NAME, tr("Remove")); - putValue(SHORT_DESCRIPTION, tr("Remove entry")); - new ImageProvider(DIALOG_IMAGES_DIR, "delete").getResource().attachImageIcon(this, true); - updateEnabledState(); - } - - protected final void updateEnabledState() { - setEnabled(activeTable.getSelectedRowCount() > 0); - } - - @Override - public void valueChanged(ListSelectionEvent e) { - updateEnabledState(); - } - - @Override - public void actionPerformed(ActionEvent e) { - int i; - while ((i = activeTable.getSelectedRow()) != -1) { - ACTIVE_MODEL.removeRow(i); - } - } - } - - private class ActivateAction extends AbstractAction implements ListSelectionListener, LayerChangeListener { - - @Serial - private static final long serialVersionUID = -452335751201424801L; - private final transient ImageResource activate; - private final transient ImageResource deactivate; - - /** - * Constructs a new {@code ActivateAction}. - */ - ActivateAction() { - putValue(NAME, tr("Activate")); - putValue(SHORT_DESCRIPTION, tr("Copy selected default entries from the list above into the list below.")); - activate = new ImageProvider("svpDown").setMaxSize(ImageProvider.ImageSizes.MENU).getResource(); - activate.attachImageIcon(this, true); - deactivate = new ImageProvider("svpUp").setMaxSize(ImageProvider.ImageSizes.MENU).getResource(); - MapWithAILayerInfo.getInstance().addListener(this); - } - - protected void updateEnabledState() { - setEnabled(defaultTable.getSelectedRowCount() > 0); - final var selected = Arrays.stream(defaultTable.getSelectedRows()).map(defaultTable::convertRowIndexToModel) - .mapToObj(MapWithAIDefaultLayerTableModel::getRow).toList(); - if (selected.stream().anyMatch(MapWithAILayerTableModel::doesNotContain)) { - activate.attachImageIcon(this, true); - putValue(NAME, tr("Activate")); - putValue(SHORT_DESCRIPTION, - tr("Copy selected default entries from the list above into the list below.")); - } else { - deactivate.attachImageIcon(this, true); - putValue(NAME, tr("Deactivate")); - putValue(SHORT_DESCRIPTION, - tr("Remove selected default entries from the list above into the list below.")); - } - } - - @Override - public void valueChanged(ListSelectionEvent e) { - updateEnabledState(); - } - - @Override - public void actionPerformed(ActionEvent e) { - int[] lines = defaultTable.getSelectedRows(); - if (lines.length == 0) { - JOptionPane.showMessageDialog(gui, tr("Please select at least one row to copy."), tr("Information"), - JOptionPane.INFORMATION_MESSAGE); - return; - } - final var selected = Arrays.stream(defaultTable.getSelectedRows()).map(defaultTable::convertRowIndexToModel) - .mapToObj(MapWithAIDefaultLayerTableModel::getRow).collect(Collectors.toCollection(ArrayList::new)); - if (selected.stream().anyMatch(MapWithAILayerTableModel::doesNotContain)) { - final var toAdd = selected.stream().filter(MapWithAILayerTableModel::doesNotContain).toList(); - activeTable.getSelectionModel().clearSelection(); - for (var info : toAdd) { - ACTIVE_MODEL.addRow(new MapWithAIInfo(info)); - int lastLine = ACTIVE_MODEL.getRowCount() - 1; - activeTable.getSelectionModel().setSelectionInterval(lastLine, lastLine); - activeTable.scrollRectToVisible(activeTable.getCellRect(lastLine, 0, true)); - } - selected.removeIf(toAdd::contains); - selected.stream().mapToInt(ACTIVE_MODEL::getRowIndex).filter(i -> i >= 0).forEach(j -> { - activeTable.getSelectionModel().addSelectionInterval(j, j); - activeTable.scrollRectToVisible(activeTable.getCellRect(j, 0, true)); - }); - } else { - selected.stream().mapToInt(ACTIVE_MODEL::getRowIndex).filter(i -> i >= 0).boxed() - .sorted(Collections.reverseOrder()).forEach(ACTIVE_MODEL::removeRow); - } - updateEnabledState(); - if (Stream.of(options).noneMatch(Options.SHOW_ACTIVE::equals)) { - MapWithAILayerInfo.getInstance().save(); - } - } - - @Override - public void changeEvent(MapWithAIInfo modified) { - GuiHelper.runInEDT(this::updateEnabledState); - } - } - - private class ReloadAction extends AbstractAction { - - @Serial - private static final long serialVersionUID = 7801339998423585685L; - - /** - * Constructs a new {@code ReloadAction}. - */ - ReloadAction() { - putValue(SHORT_DESCRIPTION, tr("Update default entries")); - new ImageProvider(DIALOG_IMAGES_DIR, "refresh").getResource().attachImageIcon(this, true); - } - - @Override - public void actionPerformed(ActionEvent evt) { - this.setEnabled(false); - MapWithAILayerInfo.getInstance().loadDefaults(true, MapWithAIDataUtils.getForkJoinPool(), false, () -> - // This needs to be run in a block to avoid race conditions. - GuiHelper.runInEDT(() -> { - defaultTable.getSelectionModel().clearSelection(); - defaultTableListener.clearMap(); - DEFAULT_MODEL.fireTableDataChanged(); - /* loading new file may change active layers */ - ACTIVE_MODEL.fireTableDataChanged(); - this.setEnabled(true); - })); - } - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/CommonSourceReader.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/CommonSourceReader.java deleted file mode 100644 index b7430017..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/CommonSourceReader.java +++ /dev/null @@ -1,89 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.io.mapwithai; - -import java.io.IOException; -import java.util.Optional; - -import org.openstreetmap.josm.io.CachedFile; -import org.openstreetmap.josm.tools.HttpClient; -import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Utils; - -import jakarta.json.Json; -import jakarta.json.stream.JsonParser; -import jakarta.json.stream.JsonParsingException; - -/** - * Read sources for MapWithAI - * - * @param The expected type - */ -public abstract class CommonSourceReader implements AutoCloseable { - private final String source; - private CachedFile cachedFile; - private boolean fastFail; - private boolean clearCache; - - CommonSourceReader(String source) { - this.source = source; - } - - /** - * Parses MapWithAI source information. - * - * @return list of source info - * @throws IOException if any I/O error occurs - */ - public Optional parse() throws IOException { - this.cachedFile = new CachedFile(this.source); - if (this.clearCache) { - this.cachedFile.clear(); - this.cachedFile = new CachedFile(this.source); - } - this.cachedFile.setFastFail(this.fastFail); - try (JsonParser reader = Json.createParser(cachedFile.setMaxAge(CachedFile.DAYS) - .setCachingStrategy(CachedFile.CachingStrategy.IfModifiedSince).getContentReader())) { - while (reader.hasNext()) { - if (reader.hasNext() && reader.next() == JsonParser.Event.START_OBJECT) { - return Optional.ofNullable(this.parseJson(reader)); - } - } - } catch (JsonParsingException jsonParsingException) { - Logging.error(jsonParsingException); - } - return Optional.empty(); - } - - /** - * Parses MapWithAI entry sources - * - * @param parser The json of the data sources. This will be in the {@link JsonParser.Event#START_OBJECT} state. - * @return The parsed entries - */ - public abstract T parseJson(JsonParser parser); - - /** - * Sets whether opening HTTP connections should fail fast, i.e., whether a - * {@link HttpClient#setConnectTimeout(int) low connect timeout} should be used. - * - * @param fastFail whether opening HTTP connections should fail fast - * @see CachedFile#setFastFail(boolean) - */ - public void setFastFail(boolean fastFail) { - this.fastFail = fastFail; - } - - @Override - public void close() throws IOException { - Utils.close(cachedFile); - } - - /** - * Indicate if cache should be ignored - * - * @param clearCache {@code true} to ignore cache - */ - public void setClearCache(boolean clearCache) { - this.clearCache = clearCache; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/ConflationSourceReader.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/ConflationSourceReader.java deleted file mode 100644 index 2da38b30..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/ConflationSourceReader.java +++ /dev/null @@ -1,66 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.io.mapwithai; - -import java.io.Closeable; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory; -import org.openstreetmap.josm.tools.Pair; - -import jakarta.json.JsonString; -import jakarta.json.JsonValue; -import jakarta.json.stream.JsonParser; - -/** - * Read conflation entries from JSON - */ -public class ConflationSourceReader extends CommonSourceReader>> - implements Closeable { - - /** - * Constructs a {@code ConflationSourceReader} from a given filename, URL or - * internal resource. - * - * @param source can be: - *
    - *
  • relative or absolute file name
  • - *
  • {@code file:///SOME/FILE} the same as above
  • - *
  • {@code http://...} a URL. It will be cached on disk.
  • - *
  • {@code resource://SOME/FILE} file from the classpath - * (usually in the current *.jar)
  • - *
  • {@code josmdir://SOME/FILE} file inside josm user data - * directory (since r7058)
  • - *
  • {@code josmplugindir://SOME/FILE} file inside josm plugin - * directory (since r7834)
  • - *
- */ - public ConflationSourceReader(String source) { - super(source); - } - - /** - * Parses MapWithAI entry sources - * - * @param parser The json of the data sources - * @return The parsed entries - */ - @Override - public Map> parseJson(JsonParser parser) { - return parser.getObjectStream().flatMap(i -> parse(i).stream()) - .collect(Collectors.groupingBy(p -> p.a, Collectors.mapping(p -> p.b, Collectors.toList()))); - } - - private static List> parse(Map.Entry entry) { - if (JsonValue.ValueType.OBJECT == entry.getValue().getValueType()) { - final var object = entry.getValue().asJsonObject(); - final var url = object.getString("url", null); - List categories = object.getJsonArray("categories").getValuesAs(JsonString.class) - .stream().map(JsonString::toString).map(MapWithAICategory::fromString).toList(); - return categories.stream().map(c -> new Pair<>(c, url)).collect(Collectors.toList()); - } - return Collections.emptyList(); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/ESRISourceReader.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/ESRISourceReader.java deleted file mode 100644 index 04d087f1..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/ESRISourceReader.java +++ /dev/null @@ -1,323 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.io.mapwithai; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ForkJoinTask; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.apache.commons.jcs3.access.CacheAccess; -import org.openstreetmap.josm.data.cache.JCSCacheManager; -import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds; -import org.openstreetmap.josm.data.preferences.LongProperty; -import org.openstreetmap.josm.io.CachedFile; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType; -import org.openstreetmap.josm.spi.preferences.Config; -import org.openstreetmap.josm.tools.HttpClient; -import org.openstreetmap.josm.tools.Logging; - -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; -import jakarta.json.Json; -import jakarta.json.JsonArray; -import jakarta.json.JsonNumber; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; -import jakarta.json.spi.JsonProvider; -import jakarta.json.stream.JsonParser; -import jakarta.json.stream.JsonParsingException; - -/** - * Take a {@link MapWithAIType#ESRI} layer and convert it to a list of "true" - * layers. - */ -public class ESRISourceReader { - private static final int INITIAL_SEARCH = 100; - /** The cache storing ESRI source information (json) */ - public static final CacheAccess SOURCE_CACHE = JCSCacheManager.getCache("mapwithai:esrisources", 5, - 50_000, new File(Config.getDirs().getCacheDirectory(true), "mapwithai").getPath()); - private static final String ACCESS_INFORMATION = "accessInformation"; - private final MapWithAIInfo source; - private boolean fastFail; - private final List ignoreConflationCategories; - private static final String JSON_QUERY_PARAM = "?f=json"; - private static final LongProperty MIRROR_MAXTIME = new LongProperty("mirror.maxtime", TimeUnit.DAYS.toSeconds(7)); - - private final JsonProvider jsonProvider = JsonProvider.provider(); - - /** - * Constructs a {@code ImageryReader} from a given filename, URL or internal - * resource. - * - * @param source can be: - *
    - *
  • relative or absolute file name
  • - *
  • {@code file:///SOME/FILE} the same as above
  • - *
  • {@code http://...} a URL. It will be cached on disk.
  • - *
  • {@code resource://SOME/FILE} file from the classpath - * (usually in the current *.jar)
  • - *
  • {@code josmdir://SOME/FILE} file inside josm user data - * directory (since r7058)
  • - *
  • {@code josmplugindir://SOME/FILE} file inside josm plugin - * directory (since r7834)
  • - *
- */ - public ESRISourceReader(MapWithAIInfo source) { - this.source = source; - this.ignoreConflationCategories = source.getConflationIgnoreCategory(); - } - - /** - * Parses MapWithAI source. - * - * @return list of source info - * @throws IOException if any I/O error occurs - */ - public List> parse() throws IOException { - final var startReplace = Pattern.compile("\\{start}"); - final var search = "/search" + JSON_QUERY_PARAM + "&sortField=added&sortOrder=desc&num=" + INITIAL_SEARCH - + "&start={start}"; - final var group = source.getId(); - var url = source.getUrl(); - if (!url.endsWith("/")) { - url = url.concat("/"); - } - - final var information = new ArrayList>(); - - final var next = new AtomicInteger(1); - final var searchUrl = new AtomicReference<>( - startReplace.matcher(search).replaceAll(Integer.toString(next.get()))); - - while (next.get() != -1) { - final var finalUrl = url + "content/groups/" + group + searchUrl.get(); - final var jsonString = getJsonString(finalUrl, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()) / 7, - this.fastFail); - if (jsonString == null) { - continue; - } - try (var parser = jsonProvider - .createParser(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) { - /* Do nothing */ - if (parser.hasNext() && parser.next() == JsonParser.Event.START_OBJECT) { - parser.getObjectStream().forEach(entry -> { - if ("nextStart".equals(entry.getKey()) && entry.getValue()instanceof JsonNumber number) { - next.set(number.intValue()); - searchUrl.set(startReplace.matcher(search).replaceAll(Integer.toString(next.get()))); - } else if ("results".equals(entry.getKey()) && entry.getValue()instanceof JsonArray features) { - for (var feature : features.getValuesAs(JsonObject.class)) { - information.add(parse(feature)); - } - } - }); - } - } catch (ClassCastException e) { - Logging.error(e); - next.set(-1); - } - } - for (var future : information) { - try { - future.join(); - future.get(1, TimeUnit.MINUTES); - } catch (InterruptedException interruptedException) { - Logging.warn(interruptedException); - Thread.currentThread().interrupt(); - } catch (ExecutionException | TimeoutException e) { - Logging.warn(e); - } - } - return information; - } - - private ForkJoinTask parse(JsonObject feature) { - // Use the initial esri server information to keep conflation info - final var newInfo = new MapWithAIInfo(source); - newInfo.setId(feature.getString("id")); - ForkJoinTask future; - if ("Feature Service".equals(feature.getString("type", ""))) { - future = ForkJoinTask.adapt(() -> newInfo.setUrl(featureService(newInfo, feature.getString("url"))), - newInfo); - } else { - newInfo.setUrl(feature.getString("url")); - future = ForkJoinTask.adapt(() -> newInfo); - } - MapWithAIDataUtils.getForkJoinPool().execute(future); - newInfo.setName(feature.getString("title", feature.getString("name"))); - final var extent = feature.getJsonArray("extent").getValuesAs(JsonArray.class).stream() - .flatMap(array -> array.getValuesAs(JsonNumber.class).stream()).map(JsonNumber::doubleValue) - .map(d -> Double.toString(d)).toArray(String[]::new); - final var imageryBounds = new ImageryBounds(String.join(",", extent[1], extent[0], extent[3], extent[2]), ","); - newInfo.setBounds(imageryBounds); - newInfo.setSourceType(MapWithAIType.ESRI_FEATURE_SERVER); - newInfo.setTermsOfUseText(feature.getString("licenseInfo", null)); - if (feature.containsKey("thumbnail")) { - newInfo.setAttributionImageURL( - source.getUrl() + "content/items/" + newInfo.getId() + "/info/" + feature.getString("thumbnail")); - } - if (feature.containsKey("groupCategories")) { - final var categories = feature.getJsonArray("groupCategories").getValuesAs(JsonString.class).stream() - .map(JsonString::getString).map(s -> s.replace("/Categories/", "")) - .map(MapWithAICategory::fromString).collect(Collectors.toCollection(ArrayList::new)); - final var category = categories.stream().filter(c -> MapWithAICategory.FEATURED != c).findFirst() - .orElse(MapWithAICategory.OTHER); - newInfo.setCategory(category); - categories.remove(category); - newInfo.setAdditionalCategories(categories); - } - - if (this.ignoreConflationCategories.contains(newInfo.getCategory())) { - newInfo.setConflation(false); - } - if (feature.containsKey(ACCESS_INFORMATION) - && feature.get(ACCESS_INFORMATION).getValueType() != JsonValue.ValueType.NULL) { - newInfo.setAttributionText(feature.getString(ACCESS_INFORMATION)); - } - newInfo.setDescription(feature.getString("snippet")); - if (newInfo.getSource() != null) { - final var sourceTag = new StringBuilder(newInfo.getSource()); - if (!sourceTag.toString().endsWith("/")) { - sourceTag.append('/'); - } - sourceTag.append(feature.getString("name", newInfo.getId())); - newInfo.setSource(sourceTag.toString()); - } - newInfo.setTermsOfUseURL("https://wiki.openstreetmap.org/wiki/Esri/ArcGIS_Datasets#License"); - return future; - } - - /** - * Get the json string for a URL - * - * @param url The URL to get - * @param fastFail Fail fast (1 second) - * @param defaultMaxAge the default max age for the response to be cached - * @return The json string, or {@code null}. - */ - @Nullable - private static String getJsonString(@Nonnull final String url, final long defaultMaxAge, final boolean fastFail) { - var jsonString = SOURCE_CACHE.get(url); - if (jsonString == null) { - HttpClient client = null; - try { - client = HttpClient.create(new URL(url)); - if (fastFail) { - client.setReadTimeout(1000); - } - final var response = client.connect(); - jsonString = response.fetchContent(); - if (jsonString != null && response.getResponseCode() < 400 && response.getResponseCode() >= 200) { - // getExpiration returns milliseconds - final long expirationTime = response.getExpiration(); - final var elementAttributes = SOURCE_CACHE.getDefaultElementAttributes(); - if (expirationTime > 0) { - elementAttributes.setMaxLife(response.getExpiration()); - } else { - elementAttributes.setMaxLife(defaultMaxAge); - } - SOURCE_CACHE.put(url, jsonString, elementAttributes); - } - } catch (final IOException e) { - Logging.error(e); - } finally { - if (client != null) { - client.disconnect(); - } - } - } - return jsonString; - } - - /** - * Get feature service information - * - * @param mapwithaiInfo The info to update - * @param url The url to get - * @return The base url for the feature service - */ - @Nullable - private String featureService(@Nonnull MapWithAIInfo mapwithaiInfo, @Nonnull String url) { - final var toGet = url.endsWith(JSON_QUERY_PARAM) ? url : url + JSON_QUERY_PARAM; - final var jsonString = getJsonString(toGet, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()), this.fastFail); - if (jsonString == null) { - return null; - } - - try (var reader = Json.createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) { - final var info = reader.readObject(); - final var layers = info.getJsonArray("layers"); - // This fixes #20551 - if (layers == null || layers.stream().noneMatch(Objects::nonNull)) { - return null; - } - // TODO use all the layers? - final var layer = layers.stream().filter(Objects::nonNull).findFirst().orElse(JsonValue.EMPTY_JSON_OBJECT) - .asJsonObject(); - if (layer.containsKey("id")) { - String partialUrl = (url.endsWith("/") ? url : url + "/") + layer.getInt("id"); - mapwithaiInfo.setReplacementTags(() -> getReplacementTags(partialUrl)); - - return partialUrl; - } - } catch (JsonParsingException e) { - Logging.error(e); - } - return null; - } - - /** - * Get the replacement tags for a feature service - * - * @param layerUrl The service url - * @return A map of replacement tags - */ - @Nonnull - private Map getReplacementTags(@Nonnull String layerUrl) { - final var toGet = layerUrl.endsWith(JSON_QUERY_PARAM) ? layerUrl : layerUrl.concat(JSON_QUERY_PARAM); - final var jsonString = getJsonString(toGet, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()), this.fastFail); - if (jsonString != null) { - try (var reader = Json - .createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) { - return reader.readObject().getJsonArray("fields").getValuesAs(JsonObject.class).stream() - .collect(Collectors.toMap(o -> o.getString("name"), ESRISourceReader::getReplacementTag)); - } - } - return Collections.emptyMap(); - } - - private static String getReplacementTag(JsonObject tag) { - if (tag.getBoolean("editable", true) && !"esriFieldTypeOID".equals(tag.getString("type", null))) { - return tag.getString("alias", ""); - } - return ""; - } - - /** - * Sets whether opening HTTP connections should fail fast, i.e., whether a - * {@link HttpClient#setConnectTimeout(int) low connect timeout} should be used. - * - * @param fastFail whether opening HTTP connections should fail fast - * @see CachedFile#setFastFail(boolean) - */ - public void setFastFail(boolean fastFail) { - this.fastFail = fastFail; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java deleted file mode 100644 index 51c8b696..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java +++ /dev/null @@ -1,135 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.io.mapwithai; - -import java.io.Closeable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.openstreetmap.josm.data.imagery.ImageryInfo; -import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.CountryUtils; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType; -import org.openstreetmap.josm.tools.Territories; - -import jakarta.json.JsonString; -import jakarta.json.JsonValue; -import jakarta.json.stream.JsonParser; - -/** - * Reader to parse the list of available MapWithAI servers from an JSON - * definition file. - *

- * The format is specified in the MapWithAI - * source. - */ -public class MapWithAISourceReader extends CommonSourceReader> implements Closeable { - /** - * Constructs a {@code MapWithAISourceReader} from a given filename, URL or - * internal resource. - * - * @param source can be: - *

    - *
  • relative or absolute file name
  • - *
  • {@code file:///SOME/FILE} the same as above
  • - *
  • {@code http://...} a URL. It will be cached on disk.
  • - *
  • {@code resource://SOME/FILE} file from the classpath - * (usually in the current *.jar)
  • - *
  • {@code josmdir://SOME/FILE} file inside josm user data - * directory (since r7058)
  • - *
  • {@code josmplugindir://SOME/FILE} file inside josm plugin - * directory (since r7834)
  • - *
- */ - public MapWithAISourceReader(String source) { - super(source); - } - - /** - * Parses MapWithAI entry sources - * - * @param parser The json of the data sources - * @return The parsed entries - */ - @Override - public List parseJson(JsonParser parser) { - return parser.getObjectStream().map(MapWithAISourceReader::parse).collect(Collectors.toList()); - } - - private static MapWithAIInfo parse(Map.Entry entry) { - final var name = entry.getKey(); - if (JsonValue.ValueType.OBJECT == entry.getValue().getValueType()) { - final var values = entry.getValue().asJsonObject(); - final var url = values.getString("url", ""); - final var type = values.getString("type", MapWithAIType.values()[0].getDefault().getTypeString()); - final var categories = values - .getString("category", MapWithAICategory.values()[0].getDefault().getCategoryString()) - .split(";", -1); - final var eula = values.getString("eula", ""); - final var conflation = values.getBoolean("conflate", false); - final var conflationUrl = values.getString("conflationUrl", null); - final var id = values.getString("id", name.replace(" ", "_")); - final var alreadyConflatedKey = values.getString("conflated_key", null); - final var countries = values.getOrDefault("countries", JsonValue.EMPTY_JSON_OBJECT); - final var bounds = getBounds(countries); - final var info = new MapWithAIInfo(name, url, type, eula, id); - info.setDefaultEntry(values.getBoolean("default", false)); - info.setParameters(values.getJsonArray("parameters")); - info.setConflationParameters(values.getJsonArray("conflationParameters")); - info.setConflation(conflation); - info.setConflationUrl(conflationUrl); - info.setSource(values.getString("source", null)); - info.setAlreadyConflatedKey(alreadyConflatedKey); - info.setAttributionText(values.getString("provider", null)); - if (categories.length > 0) { - info.setCategory(MapWithAICategory.fromString(categories[0])); - if (categories.length > 1) { - info.setAdditionalCategories(Stream.of(categories).skip(1).map(MapWithAICategory::fromString) - .collect(Collectors.toList())); - } - } - if (values.containsKey("conflation_ignore_categories")) { - final var ignore = values.getJsonArray("conflation_ignore_categories"); - for (MapWithAICategory cat : ignore.getValuesAs(JsonString.class).stream().map(JsonString::getString) - .map(MapWithAICategory::fromString).toList()) { - info.addConflationIgnoreCategory(cat); - } - } - if (values.containsKey("terms_of_use_url")) { - info.setTermsOfUseURL(values.getString("terms_of_use_url")); - } - if (values.containsKey("privacy_policy_url")) { - info.setPrivacyPolicyURL(values.getString("privacy_policy_url")); - } - if (!bounds.isEmpty()) { - ImageryBounds bound = bounds.get(0); - bounds.remove(0); - bounds.forEach(bound::extend); - bounds.forEach(b -> b.getShapes().forEach(bound::addShape)); - info.setBounds(bound); - } - return info; - } - return new MapWithAIInfo(name); - } - - private static List getBounds(JsonValue countries) { - if (JsonValue.ValueType.OBJECT == countries.getValueType()) { - Set codes = Territories.getKnownIso3166Codes(); - List bounds = new ArrayList<>(); - for (Map.Entry country : countries.asJsonObject().entrySet()) { - if (codes.contains(country.getKey())) { - CountryUtils.getCountryShape(country.getKey()).ifPresent(bounds::add); - } - } - return bounds; - } - return new ArrayList<>(); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/OvertureSourceReader.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/OvertureSourceReader.java deleted file mode 100644 index 6c51b50d..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/OvertureSourceReader.java +++ /dev/null @@ -1,146 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.io.mapwithai; - -import java.io.Closeable; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.Objects; -import java.util.stream.Stream; - -import org.openstreetmap.josm.data.Bounds; -import org.openstreetmap.josm.data.imagery.ImageryInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType; -import org.openstreetmap.josm.plugins.pmtiles.lib.PMTiles; -import org.openstreetmap.josm.tools.Logging; - -import jakarta.annotation.Nullable; -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; -import jakarta.json.stream.JsonParser; - -/** - * Read data from overture sources - */ -public class OvertureSourceReader extends CommonSourceReader> implements Closeable { - private final MapWithAIInfo source; - - public OvertureSourceReader(MapWithAIInfo source) { - super(source.getUrl()); - this.source = source; - } - - @Override - public List parseJson(JsonParser jsonParser) { - final var jsonObject = jsonParser.getObject(); - if (jsonObject.containsKey("releases")) { - return parseRoot(jsonObject); - } - return Collections.emptyList(); - } - - private List parseRoot(JsonObject jsonObject) { - final var info = new ArrayList(6 * 4); - final var releases = jsonObject.get("releases"); - final var baseUri = URI.create(this.source.getUrl()).resolve("./"); // safe since we created an URI from the source to get to this point - if (releases instanceof JsonArray rArray) { - rArray.parallelStream().flatMap(value -> parseReleases(baseUri, value)).filter(Objects::nonNull) - .forEachOrdered(info::add); - } - info.trimToSize(); - return info; - } - - private Stream parseReleases(URI baseUri, JsonValue value) { - if (value instanceof JsonObject release && release.containsKey("release_id") && release.containsKey("files")) { - final var id = release.get("release_id"); - final var files = release.get("files"); - if (id instanceof JsonString sId && files instanceof JsonArray fArray) { - final String releaseId = sId.getString(); - return fArray.parallelStream().map(file -> parseFile(baseUri, releaseId, file)); - } - } - return Stream.empty(); - } - - /** - * Parse the individual file from the files array - * @param baseUri The base URI (if the href is relative) - * @param releaseId The release id to differentiate it from other releases with the same theme - * @param file The file object - * @return The info, if it was parsed. Otherwise {@code null}. - */ - @Nullable - private MapWithAIInfo parseFile(URI baseUri, String releaseId, JsonValue file) { - if (file instanceof JsonObject fObj && fObj.containsKey("theme") && fObj.containsKey("href")) { - final JsonValue vTheme = fObj.get("theme"); - final JsonValue vHref = fObj.get("href"); - try { - if (vTheme instanceof JsonString sTheme && vHref instanceof JsonString href) { - final var theme = sTheme.getString(); - final URI uri; - if (href.getString().startsWith("./") || href.getString().startsWith("../")) { - uri = baseUri.resolve(href.getString()); - } else { - uri = new URI(href.getString()); - } - return buildSource(uri, releaseId, theme); - } - } catch (URISyntaxException uriSyntaxException) { - Logging.debug(uriSyntaxException); - } - } - return null; - } - - private MapWithAIInfo buildSource(URI uri, String releaseId, String theme) { - final var info = new MapWithAIInfo(this.source); - info.setUrl(uri.toString()); - info.setName(this.source.getName() + ": " + theme + " - " + releaseId); - if ("addresses".equals(theme)) { - info.setCategory(MapWithAICategory.ADDRESS); - } else if ("buildings".equals(theme)) { - info.setCategory(MapWithAICategory.BUILDING); - } else { - info.setCategory(MapWithAICategory.OTHER); - } - // Addresses and places are "interesting". Only removing "transportation" since that currently causes crashes. - if ("transportation".equals(theme)) { - return null; - } - final var categories = EnumSet.of(this.source.getCategory(), - this.source.getAdditionalCategories().toArray(MapWithAICategory[]::new)); - categories.removeIf(MapWithAICategory.OTHER::equals); - info.setAdditionalCategories(new ArrayList<>(categories)); - info.setId(info.getName()); - if (uri.getPath().endsWith(".pmtiles")) { - info.setSourceType(MapWithAIType.PMTILES); - // Set additional information - try { - final var header = PMTiles.readHeader(uri); - final var metadata = PMTiles.readMetadata(header); - final var bounds = new Bounds(header.minLatitude(), header.minLongitude(), header.maxLatitude(), - header.maxLongitude()); - info.setBounds(new ImageryInfo.ImageryBounds(bounds.encodeAsString(","), ",")); - if (metadata.containsKey("name") && metadata.get("name")instanceof JsonString name) { - info.setName(name.getString() + " - " + releaseId); - } - if (metadata.containsKey("description") - && metadata.get("description")instanceof JsonString description) { - info.setDescription(description.getString()); - } - } catch (IOException ioException) { - Logging.error(ioException); - } - } - return info; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/spi/preferences/IMapWithAIUrls.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/spi/preferences/IMapWithAIUrls.java deleted file mode 100644 index dde9e017..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/spi/preferences/IMapWithAIUrls.java +++ /dev/null @@ -1,32 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.spi.preferences; - -/** - * Interface for a provider of certain URLs. Modelled after - * {@link org.openstreetmap.josm.spi.preferences.IUrls}. - * - * @author Taylor Smock - */ -public interface IMapWithAIUrls { - - /** - * Get the conflation server json URL - * - * @return The URL with additional conflation servers - */ - String getConflationServerJson(); - - /** - * Get the URL for MapWithAI sources - * - * @return The URL with source information - */ - String getMapWithAISourcesJson(); - - /** - * Get the URL for the MapWithAI paintstyle - * - * @return The URL to use to get the paint style - */ - String getMapWithAIPaintStyle(); -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/spi/preferences/MapWithAIConfig.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/spi/preferences/MapWithAIConfig.java deleted file mode 100644 index 7a3f0dc8..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/spi/preferences/MapWithAIConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.spi.preferences; - -import java.util.Objects; - -/** - * Class to hold the global preferences objects. Modeled after - * {@link org.openstreetmap.josm.spi.preferences.Config}. - * - * @author Taylor Smock - */ -public final class MapWithAIConfig { - private MapWithAIConfig() { - // Hide constructor - } - - private static IMapWithAIUrls urls; - - /** - * Get class that provides the value of certain URLs - * - * @return the global {@link IMapWithAIUrls} instance - */ - public static IMapWithAIUrls getUrls() { - return urls; - } - - /** - * Install the global URLs provider. - * - * @param urls the global URLs provider instance to set (must not be null) - */ - public static void setUrlsProvider(IMapWithAIUrls urls) { - MapWithAIConfig.urls = Objects.requireNonNull(urls); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/spi/preferences/MapWithAIUrls.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/spi/preferences/MapWithAIUrls.java deleted file mode 100644 index fb1fd09f..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/spi/preferences/MapWithAIUrls.java +++ /dev/null @@ -1,48 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.spi.preferences; - -/** - * Store the MapWithAI URLs - */ -public final class MapWithAIUrls implements IMapWithAIUrls { - /** The base url */ - private static final String BASE_URL = "https://josm.github.io/MapWithAI/"; - /** The default url for additional conflation servers */ - private static final String DEFAULT_CONFLATION_JSON = BASE_URL + "json/conflation_servers.json"; - /** The default URL for the MapWithAI sources */ - private static final String DEFAULT_MAPWITHAI_SOURCES_JSON = BASE_URL + "json/sources.json"; - /** The default url for the MapWithAI paint style */ - private static final String DEFAULT_PAINT_STYLE_RESOURCE_URL = "https://josm.openstreetmap.de/josmfile?page=Styles/MapWithAI&zip=1"; - - private static class InstanceHolder { - static final MapWithAIUrls INSTANCE = new MapWithAIUrls(); - } - - /** - * Returns the unique instance. - * - * @return the unique instance - */ - public static MapWithAIUrls getInstance() { - return MapWithAIUrls.InstanceHolder.INSTANCE; - } - - @Override - public String getConflationServerJson() { - return DEFAULT_CONFLATION_JSON; - } - - @Override - public String getMapWithAISourcesJson() { - return DEFAULT_MAPWITHAI_SOURCES_JSON; - } - - @Override - public String getMapWithAIPaintStyle() { - return DEFAULT_PAINT_STYLE_RESOURCE_URL; - } - - private MapWithAIUrls() { - // Hide the constructor - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/Access.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/Access.java deleted file mode 100644 index f4f00c95..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/Access.java +++ /dev/null @@ -1,668 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.tools; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import org.openstreetmap.josm.data.osm.OsmPrimitive; - -/** - * Access tag related utilities - * - * @see Key:access - * - * @author Taylor Smock - * @since xxx - */ -public final class Access { - /** - * Holds access tags to avoid typos - */ - public enum AccessTags { - /** Air, land, and sea */ - ALL_TRANSPORT_TYPE("all"), - - /** - * Key:access - */ - ACCESS_KEY("access", ALL_TRANSPORT_TYPE), - - // Access tag values - /** - * Tag:access%3Dyes - */ - YES("yes"), - /** - * Tag:access%3Dofficial - */ - OFFICIAL("official"), - /** - * Tag:access%3Ddesignated - */ - DESIGNATED("designated"), - /** - * Tag:access%3Ddestination - */ - DESTINATION("destination"), - /** - * Tag:access%3Ddelivery - */ - DELIVERY("delivery"), - /** - * Tag:access%3Dcustomers - */ - CUSTOMERS("customers"), - /** - * Tag:access%3Dpermissive - */ - PERMISSIVE("permissive"), - /** - * Tag:access%3Dagricultural - */ - AGRICULTURAL("agricultural"), - /** - * Tag:access%3Dforestry - */ - FORESTRY("forestry"), - /** - * Tag:access%3Dprivate - */ - PRIVATE("private"), - /** - * Tag:access%3Dno - */ - NO("no"), - /** - * Tag:access%3Ddiscouraged - */ - DISCOURAGED("discouraged"), - /** - * Tag:access%3Duse_sidepath - */ - USE_SIDEPATH("use_sidepath"), - /** - * Tag:access%3Ddismount - */ - DISMOUNT("dismount"), - // Land - /** Land transport types */ - LAND_TRANSPORT_TYPE("land", ALL_TRANSPORT_TYPE), - /** - * Key:vehicle - */ - VEHICLE("vehicle", LAND_TRANSPORT_TYPE), - /** - * Key:motor_vehicle - */ - MOTOR_VEHICLE("motor_vehicle", LAND_TRANSPORT_TYPE), - /** - * Key:trailer - */ - TRAILER("trailer", LAND_TRANSPORT_TYPE), - /** Key:foot */ - FOOT("foot", LAND_TRANSPORT_TYPE), - /** Key:ski */ - SKI("ski", LAND_TRANSPORT_TYPE), - /** - * Key:inline_skates - */ - INLINE_SKATES("inline_skates", LAND_TRANSPORT_TYPE), - /** - * Key:ice_skates - */ - ICE_SKATES("ice_skates", LAND_TRANSPORT_TYPE), - /** - * Key:horse - */ - HORSE("horse", LAND_TRANSPORT_TYPE), - /** - * Key:bicycle - */ - BICYCLE("bicycle", LAND_TRANSPORT_TYPE), - /** - * Key:carriage - */ - CARRIAGE("carriage", LAND_TRANSPORT_TYPE), - /** - * Key:caravan - */ - CARAVAN("caravan", LAND_TRANSPORT_TYPE), - /** - * Key:motorcycle - */ - MOTORCYCLE("motorcycle", LAND_TRANSPORT_TYPE), - /** - * Key:moped - */ - MOPED("moped", LAND_TRANSPORT_TYPE), - /** Key:mofa */ - MOFA("mofa", LAND_TRANSPORT_TYPE), - /** - * Key:motorcar - */ - MOTORCAR("motorcar", LAND_TRANSPORT_TYPE), - /** - * Key:motorhome - */ - MOTORHOME("motorhome", LAND_TRANSPORT_TYPE), - /** Key:psv */ - PSV("psv", LAND_TRANSPORT_TYPE), - /** Key:bus */ - BUS("bus", LAND_TRANSPORT_TYPE), - /** Key:taxi */ - TAXI("taxi", LAND_TRANSPORT_TYPE), - /** - * Key:tourist_bus - */ - TOURIST_BUS("tourist_bus", LAND_TRANSPORT_TYPE), - /** - * Key:goods - */ - GOODS("goods", LAND_TRANSPORT_TYPE), - /** Key:hgv */ - HGV("hgv", LAND_TRANSPORT_TYPE), - /** Key:atv */ - ATV("atv", LAND_TRANSPORT_TYPE), - /** - * Key:snowmobile - */ - SNOWMOBILE("snowmobile", LAND_TRANSPORT_TYPE), - /** - * Key:hgv_articulated - */ - HGV_ARTICULATED("hgv_articulated", LAND_TRANSPORT_TYPE), - /** Key:ski */ - SKI_NORDIC("ski:nordic", LAND_TRANSPORT_TYPE), - /** Key:ski */ - SKI_ALPINE("ski:alpine", LAND_TRANSPORT_TYPE), - /** Key:ski */ - SKI_TELEMARK("ski:telemark", LAND_TRANSPORT_TYPE), - /** - * Key:coach - */ - COACH("coach", LAND_TRANSPORT_TYPE), - /** - * Key:golf_cart - */ - GOLF_CART("golf_cart", LAND_TRANSPORT_TYPE), - /** - * Key:minibus - */ - MINIBUS("minibus", LAND_TRANSPORT_TYPE), - /** - * Key:share_taxi - */ - SHARE_TAXI("share_taxi", LAND_TRANSPORT_TYPE), - /** Key:hov */ - HOV("hov", LAND_TRANSPORT_TYPE), - /** - * Key:car_sharing - */ - CAR_SHARING("car_sharing", LAND_TRANSPORT_TYPE), - /** - * Routers should default to {@code yes}, regardless of higher access rules, - * assuming it is navigatible by vehicle - * - * Key:emergency - */ - EMERGENCY("emergency", LAND_TRANSPORT_TYPE), - /** - * Key:hazmat - */ - HAZMAT("hazmat", LAND_TRANSPORT_TYPE), - /** - * Key:disabled - */ - DISABLED("disabled", LAND_TRANSPORT_TYPE), - - // Water - /** Water transport type */ - WATER_TRANSPORT_TYPE("water", ALL_TRANSPORT_TYPE), - /** - * Key:swimming - */ - SWIMMING("swimming", WATER_TRANSPORT_TYPE), - /** Key:boat */ - BOAT("boat", WATER_TRANSPORT_TYPE), - /** - * Key:fishing_vessel - */ - FISHING_VESSEL("fishing_vessel", WATER_TRANSPORT_TYPE), - /** Key:ship */ - SHIP("ship", WATER_TRANSPORT_TYPE), - /** - * Key:motorboat - */ - MOTORBOAT("motorboat", WATER_TRANSPORT_TYPE), - /** - * Key:sailboat - */ - SAILBOAT("sailboat", WATER_TRANSPORT_TYPE), - /** - * Key:canoe - */ - CANOE("canoe", WATER_TRANSPORT_TYPE), - /** - * Key:passenger - */ - PASSENGER("passenger", WATER_TRANSPORT_TYPE), - /** - * Key:cargo - */ - CARGO("cargo", WATER_TRANSPORT_TYPE), - /** Key:isps */ - ISPS("isps", WATER_TRANSPORT_TYPE), - /** Key:bulk */ - BULK("bulk", WATER_TRANSPORT_TYPE), - /** - * Key:tanker - */ - TANKER("tanker", WATER_TRANSPORT_TYPE), - /** - * Key:container - */ - CONTAINER("container", WATER_TRANSPORT_TYPE), - /** Key:imdg */ - IMDG("imdg", WATER_TRANSPORT_TYPE), - /** - * Key:tanker - */ - TANKER_GAS("tanker:gas", WATER_TRANSPORT_TYPE), - /** - * Key:tanker - */ - TANKER_OIL("tanker:oil", WATER_TRANSPORT_TYPE), - /** - * Key:tanker - */ - TANKER_CHEMICAL("tanker:chemical", WATER_TRANSPORT_TYPE), - /** - * Key:tanker - */ - TANKER_SINGLEHULL("tanker:singlehull", WATER_TRANSPORT_TYPE), - - // Trains - /** Rail transport type */ - RAIL_TRANSPORT_TYPE("rail", ALL_TRANSPORT_TYPE), - /** - * Key:train - */ - TRAIN("train", RAIL_TRANSPORT_TYPE); - - /** Used to avoid array instantiations */ - private static AccessTags[] allValues = values(); - - private final String key; - private final AccessTags type; - - AccessTags(String key) { - this.key = key; - this.type = null; - } - - AccessTags(String key, AccessTags type) { - this.key = key; - this.type = type; - } - - /** - * Get the key for the enum - * - * @return The OpenStreetMap key - */ - public String getKey() { - return key; - } - - /** - * Get the generic type of transport - * - * @return The AccessTags transport type - * (RAIL_TRANSPORT_TYPE/WATER_TRANSPORT_TYPE/etc) - */ - public AccessTags getTransportType() { - return type; - } - - /** - * Check if this is a parent transport type (air/sea/water/all) - * - * @param potentialDescendant The AccessTags that we want to check - * @return true if valueOf is a child transport type of this - */ - public boolean parentOf(AccessTags potentialDescendant) { - AccessTags tmp = potentialDescendant; - while (tmp != null && tmp != this) { - tmp = tmp.getTransportType(); - } - return tmp == this; - } - - /** - * Get the enum that matches the mode - * - * @param childrenMode The mode to get the access tag - * @return The AccessTags enum that matches the childrenMode, or null - */ - public static AccessTags get(String childrenMode) { - for (AccessTags value : allValues) { - if (value.getKey().equalsIgnoreCase(childrenMode)) { - return value; - } - } - return null; - } - - /** - * Get access tags that match a certain type - * - * @param type {@link AccessTags#WATER_TRANSPORT_TYPE}, - * {@link AccessTags#LAND_TRANSPORT_TYPE}, - * {@link AccessTags#RAIL_TRANSPORT_TYPE}, or - * {@link AccessTags#ALL_TRANSPORT_TYPE} - * @return A collection of access tags that match the given transport type - */ - public static Collection getByTransportType(AccessTags type) { - return Arrays.stream(allValues).filter(type::parentOf).collect(Collectors.toList()); - } - } - - /** - * The key for children modes for the map, see {@link Access#accessMethods} - */ - public static final String CHILDREN = "children"; - /** The key for parent modes for the map, see {@link Access#accessMethods} */ - public static final String PARENT = "parent"; - /** This set has keys that indicate that access is possible */ - private static final Set POSITIVE_ACCESS = new HashSet<>( - Arrays.asList(AccessTags.YES, AccessTags.OFFICIAL, AccessTags.DESIGNATED, AccessTags.DESTINATION, - AccessTags.DELIVERY, AccessTags.CUSTOMERS, AccessTags.PERMISSIVE, AccessTags.AGRICULTURAL, - AccessTags.FORESTRY).stream().map(AccessTags::getKey).collect(Collectors.toSet())); - /** This set has all basic restriction values (yes/no/permissive/private/...) */ - private static final Set RESTRICTION_VALUES = new HashSet<>(Arrays.asList(AccessTags.PRIVATE, AccessTags.NO) - .stream().map(AccessTags::getKey).collect(Collectors.toSet())); - /** This set has transport modes (access/foot/ski/motor_vehicle/vehicle/...) */ - private static final Set TRANSPORT_MODES = new HashSet<>(Arrays.asList(AccessTags.ACCESS_KEY, - AccessTags.FOOT, AccessTags.SKI, AccessTags.INLINE_SKATES, AccessTags.ICE_SKATES, AccessTags.HORSE, - AccessTags.VEHICLE, AccessTags.BICYCLE, AccessTags.CARRIAGE, AccessTags.TRAILER, AccessTags.CARAVAN, - AccessTags.MOTOR_VEHICLE, AccessTags.MOTORCYCLE, AccessTags.MOPED, AccessTags.MOFA, AccessTags.MOTORCAR, - AccessTags.MOTORHOME, AccessTags.PSV, AccessTags.BUS, AccessTags.TAXI, AccessTags.TOURIST_BUS, - AccessTags.GOODS, AccessTags.HGV, AccessTags.AGRICULTURAL, AccessTags.ATV, AccessTags.SNOWMOBILE, - AccessTags.HGV_ARTICULATED, AccessTags.SKI_NORDIC, AccessTags.SKI_ALPINE, AccessTags.SKI_TELEMARK, - AccessTags.COACH, AccessTags.GOLF_CART - /* - * ,"minibus","share_taxi","hov","car_sharing","emergency","hazmat","disabled" - */).stream().map(AccessTags::getKey).collect(Collectors.toSet())); - - /** - * Map<Access Method, Map<Parent/Child, List<Access Methods>>> - */ - private static final Map>> accessMethods = new HashMap<>(); - static { - RESTRICTION_VALUES.addAll(POSITIVE_ACCESS); - defaultInheritance(); - } - - private Access() { - // Hide the constructor - } - - /** - * Create the default access inheritance, as defined at - * Key:access#Transport_mod_restrictions - */ - private static void defaultInheritance() { - addMode(null, AccessTags.ACCESS_KEY); - - // Land - addModes(AccessTags.ACCESS_KEY, AccessTags.FOOT, AccessTags.SKI, AccessTags.INLINE_SKATES, - AccessTags.ICE_SKATES, AccessTags.HORSE, AccessTags.VEHICLE); - addModes(AccessTags.SKI, AccessTags.SKI_NORDIC, AccessTags.SKI_ALPINE, AccessTags.SKI_TELEMARK); - addModes(AccessTags.VEHICLE, AccessTags.BICYCLE, AccessTags.CARRIAGE, AccessTags.TRAILER, - AccessTags.MOTOR_VEHICLE); - addModes(AccessTags.TRAILER, AccessTags.CARAVAN); - addModes(AccessTags.MOTOR_VEHICLE, AccessTags.MOTORCYCLE, AccessTags.MOPED, AccessTags.MOFA, - AccessTags.MOTORCAR, AccessTags.MOTORHOME, AccessTags.TOURIST_BUS, AccessTags.COACH, AccessTags.GOODS, - AccessTags.HGV, AccessTags.AGRICULTURAL, AccessTags.GOLF_CART, AccessTags.ATV, AccessTags.SNOWMOBILE, - AccessTags.PSV, AccessTags.HOV, AccessTags.CAR_SHARING, AccessTags.EMERGENCY, AccessTags.HAZMAT, - AccessTags.DISABLED); - addMode(AccessTags.HGV, AccessTags.HGV_ARTICULATED); - addModes(AccessTags.PSV, AccessTags.BUS, AccessTags.MINIBUS, AccessTags.SHARE_TAXI, AccessTags.TAXI); - - // Water - addModes(AccessTags.ACCESS_KEY, AccessTags.SWIMMING, AccessTags.BOAT, AccessTags.FISHING_VESSEL, - AccessTags.SHIP); - addModes(AccessTags.BOAT, AccessTags.MOTORBOAT, AccessTags.SAILBOAT, AccessTags.CANOE); - addModes(AccessTags.SHIP, AccessTags.PASSENGER, AccessTags.CARGO, AccessTags.ISPS); - addModes(AccessTags.CARGO, AccessTags.BULK, AccessTags.TANKER, AccessTags.CONTAINER, AccessTags.IMDG); - addModes(AccessTags.TANKER, AccessTags.TANKER_GAS, AccessTags.TANKER_OIL, AccessTags.TANKER_CHEMICAL, - AccessTags.TANKER_SINGLEHULL); - - // Rail - addModes(AccessTags.ACCESS_KEY, AccessTags.TRAIN); - } - - /** - * Add multiple modes with a common parent - * - * @param parent The parent of all the modes - * @param modes The modes to add - */ - public static void addModes(AccessTags parent, AccessTags... modes) { - for (AccessTags mode : modes) { - addMode(parent, mode); - } - } - - /** - * Add modes to a list, modifying parents as needed - * - * @param mode The mode to be added - * @param parent The parent of the mode - */ - public static void addMode(AccessTags parent, AccessTags mode) { - Objects.requireNonNull(mode, "Mode must not be null"); - if (parent != null) { - Map> parentMap = accessMethods.getOrDefault(parent.getKey(), new HashMap<>()); - accessMethods.putIfAbsent(parent.getKey(), parentMap); - List parentChildren = parentMap.getOrDefault(CHILDREN, new ArrayList<>()); - if (!parentChildren.contains(mode.getKey())) { - parentChildren.add(mode.getKey()); - } - parentMap.putIfAbsent(CHILDREN, parentChildren); - } - Map> modeMap = accessMethods.getOrDefault(mode.getKey(), new HashMap<>()); - accessMethods.putIfAbsent(mode.getKey(), modeMap); - List modeParent = modeMap.getOrDefault(PARENT, - Collections.singletonList(parent == null ? null : parent.getKey())); - modeMap.putIfAbsent(PARENT, modeParent); - } - - /** - * Get the number of parents a mode has - * - * @param mode The mode with parents - * @return The number of parents the mode has - */ - public static int depth(String mode) { - String tempMode = mode; - int maxCount = accessMethods.size(); - while (tempMode != null && maxCount > 0) { - tempMode = accessMethods.getOrDefault(tempMode, Collections.emptyMap()) - .getOrDefault(PARENT, Collections.emptyList()).get(0); - if (tempMode != null) { - maxCount--; - } - } - return accessMethods.size() - maxCount; - } - - /** - * Expand access modes to cover the children of that access mode (currently only - * supports the default hierarchy) - * - * @param mode The transport mode - * @param access The access value (the children transport modes inherit this - * value) - * @return A map of the mode and its children (does not include parents) - */ - public static Map expandAccessMode(String mode, String access) { - return expandAccessMode(mode, access, AccessTags.ALL_TRANSPORT_TYPE); - } - - /** - * Expand access modes to cover the children of that access mode (currently only - * supports the default hierarchy) - * - * @param mode The transport mode - * @param access The access value (the children transport modes inherit - * this value) - * @param transportType {@link AccessTags#ALL_TRANSPORT_TYPE}, - * {@link AccessTags#LAND_TRANSPORT_TYPE}, - * {@link AccessTags#WATER_TRANSPORT_TYPE}, - * {@link AccessTags#RAIL_TRANSPORT_TYPE} - * @return A map of the mode and its children (does not include parents) - */ - public static Map expandAccessMode(String mode, String access, AccessTags transportType) { - Map accessModes = new HashMap<>(); - accessModes.put(mode, access); - if (accessMethods.containsKey(mode)) { - for (String childrenMode : accessMethods.getOrDefault(mode, Collections.emptyMap()).getOrDefault(CHILDREN, - Collections.emptyList())) { - if (transportType.parentOf(AccessTags.get(childrenMode))) { - accessModes.putAll(expandAccessMode(childrenMode, access, transportType)); - } - } - } - return accessModes; - } - - /** - * Merge two access maps (more specific wins) - * - * @param map1 A map with access values (see {@link Access#expandAccessMode}) - * @param map2 A map with access values (see {@link Access#expandAccessMode}) - * @return The merged map - */ - public static Map mergeMaps(Map map1, Map map2) { - Map merged; - if (map1.keySet().containsAll(map2.keySet())) { - merged = new HashMap<>(map1); - merged.putAll(map2); - } else { // if they don't overlap or if map2 contains all of map1 - merged = new HashMap<>(map2); - merged.putAll(map1); - } - return merged; - } - - /** - * Get the set of values that can generally be considered to be accessible - * - * @return A set of values that can be used for routing purposes (unmodifiable) - */ - public static Set getPositiveAccessValues() { - return Collections.unmodifiableSet(POSITIVE_ACCESS); - } - - /** - * Get the valid restriction values ({@code unknown} is not included). See - * - * @return Valid values for restrictions (unmodifiable) Key:access#List_of_possible_values - */ - public static Set getRestrictionValues() { - return Collections.unmodifiableSet(RESTRICTION_VALUES); - } - - /** - * Get the valid transport modes. See - * - * @return Value transport modes (unmodifiable) Key:access#Transport_mode_restrictions - */ - public static Set getTransportModes() { - return Collections.unmodifiableSet(TRANSPORT_MODES); - } - - /** - * Get the implied access values for a primitive - * - * @param primitive A primitive with access values - * @param transportType {@link AccessTags#ALL_TRANSPORT_TYPE}, - * {@link AccessTags#LAND_TRANSPORT_TYPE}, - * {@link AccessTags#WATER_TRANSPORT_TYPE}, - * {@link AccessTags#RAIL_TRANSPORT_TYPE} - * @return The implied access values (for example, "hgv=designated" adds - * "hgv_articulated=designated") - */ - public static Map getAccessValues(OsmPrimitive primitive, AccessTags transportType) { - Map accessValues = new HashMap<>(); - TRANSPORT_MODES.stream().filter(primitive::hasKey) - .map(mode -> expandAccessMode(mode, primitive.get(mode), transportType)).forEach(modeAccess -> { - Map tMap = mergeMaps(accessValues, modeAccess); - accessValues.clear(); - accessValues.putAll(tMap); - }); - return accessValues; - } - - /** - * Expand a map of access values - * - * @param accessValues A map of mode, access type values - * @return The expanded access values - */ - public static Map expandAccessValues(Map accessValues) { - Map modes = new HashMap<>(); - List> list = accessValues.entrySet().stream() - .map(entry -> expandAccessMode(entry.getKey(), entry.getValue())) - .sorted(Comparator.comparingInt(Map::size)).collect(Collectors.toCollection(ArrayList::new)); - Collections.reverse(list); - for (Map access : list) { - modes = mergeMaps(modes, access); - } - return modes; - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/BlacklistUtils.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/BlacklistUtils.java deleted file mode 100644 index 260c04cf..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/BlacklistUtils.java +++ /dev/null @@ -1,79 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.tools; - -import java.io.IOException; - -import org.openstreetmap.josm.io.CachedFile; -import org.openstreetmap.josm.io.NetworkManager; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.tools.Logging; - -import jakarta.json.Json; -import jakarta.json.JsonArray; -import jakarta.json.JsonException; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; - -/** - * Check if this version has been blacklisted (i.e., bad data is uploaded) - * - * @author Taylor Smock - * - */ -public final class BlacklistUtils { - static final String DEFAULT_BLACKLIST_URL = "https://josm.github.io/MapWithAI/json/blacklisted_versions.json"; - private static String blacklistUrl = DEFAULT_BLACKLIST_URL; - - private BlacklistUtils() { - // Don't instantiate - } - - /** - * Check if this version is known to make bad data - * - * @return {@code true} if no data should be uploaded or added. Defaults to - * {@code true}. - */ - public static boolean isBlacklisted() { - final var version = MapWithAIPlugin.getVersionInfo(); - final var blacklist = new CachedFile(blacklistUrl); - try (var bufferedReader = blacklist.getContentReader(); var reader = Json.createReader(bufferedReader)) { - final var structure = reader.read(); - if (structure instanceof JsonArray array) { - return array.stream().filter(v -> v.getValueType() == JsonValue.ValueType.STRING) - .map(v -> ((JsonString) v).getString()).anyMatch(version::equals); - } else if (structure instanceof JsonObject object) { - return object.containsKey(version); - } - } catch (IOException | JsonException e) { - try { - blacklist.clear(); - } catch (IOException e1) { - Logging.error(e1); - } - Logging.error(e); - } finally { - blacklist.close(); - } - return true; - } - - /** - * Set a new blacklist URL. Should only be used for testing. - * - * @param url The url to check. - */ - static void setBlacklistUrl(String url) { - blacklistUrl = url; - } - - /** - * Check if we can reach the URL for the known-bad version list. - * - * @return {@code true} if the configured url is offline - */ - public static boolean isOffline() { - return NetworkManager.isOffline(blacklistUrl); - } -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/MapPaintUtils.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/MapPaintUtils.java deleted file mode 100644 index db47919b..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/MapPaintUtils.java +++ /dev/null @@ -1,312 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.tools; - -import java.awt.Color; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.zip.CRC32; -import java.util.zip.ZipEntry; -import java.util.zip.ZipException; -import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; - -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.IPrimitive; -import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; -import org.openstreetmap.josm.gui.mappaint.StyleSetting; -import org.openstreetmap.josm.gui.mappaint.StyleSetting.StyleSettingGroup; -import org.openstreetmap.josm.gui.mappaint.StyleSource; -import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.io.CachedFile; -import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig; -import org.openstreetmap.josm.tools.ColorHelper; -import org.openstreetmap.josm.tools.Logging; - -/** - * Utils for the MapWithAI paint style - */ -public final class MapPaintUtils { - /** The default url for the MapWithAI paint style */ - private static final Pattern TEST_PATTERN = Pattern - .compile("^https?://(www\\.)?localhost[:\\d]*/josmfile\\?page=Styles/MapWithAI&zip=1$"); - - private static final String SOURCE_KEY = "source"; - private static final String MAPWITHAI_SOURCE_KEY = "mapwithai:" + SOURCE_KEY; - - private static final String MAPWITHAI_MAPCSS_ZIP_NAME = "Styles_MapWithAI-style.mapcss"; - private static final double CRC_DIVIDE_TO_TEN_K_MAX = 429496.7296; - - /** - * Safe colors - */ - public enum SafeColors { - RED(Color.RED), ORANGE(Color.ORANGE), GOLD(Objects.requireNonNull(ColorHelper.html2color("#ffd700"))), LIME( - Objects.requireNonNull(ColorHelper.html2color("#00ff00"))), CYAN(Color.CYAN), DODGER_BLUE( - Objects.requireNonNull(ColorHelper.html2color("#1e90ff"))), AI_MAGENTA( - Objects.requireNonNull(ColorHelper.html2color("#ff26d4"))), PINK( - Color.PINK), LIGHT_GREY( - Objects.requireNonNull(ColorHelper.html2color("#d3d3d3"))), LINEN( - Objects.requireNonNull(ColorHelper.html2color("#faf0e6"))); - - private final Color color; - - SafeColors(Color color) { - this.color = new Color(color.getRGB()); - } - - /** - * Get the safe color - * - * @return The safe color - */ - public Color getColor() { - return new Color(this.color.getRGB()); - } - } - - private MapPaintUtils() { - // This is a utils class. Don't allow constructing. - } - - /** - * Add a paintstyle from the jar - * - * @return The added paint style, or the already existing paint style. May be - * {@code null} in some unusual circumstances. - */ - public static synchronized StyleSource addMapWithAIPaintStyles() { - // Remove old url's that were automatically added -- remove after Jan 01, 2020 - final List oldUrls = Arrays.asList( - Pattern.compile("https://gitlab.com/(gokaart/JOSM_MapWithAI|smocktaylor/rapid)" - + "/raw/master/src/resources/styles/standard/(mapwithai|rapid).mapcss"), - TEST_PATTERN, Pattern.compile("resource://styles/standard/mapwithai.mapcss")); - new ArrayList<>(MapPaintStyles.getStyles().getStyleSources()).stream() - .filter(style -> oldUrls.stream().anyMatch(p -> p.matcher(style.url).matches())) - .forEach(MapPaintStyles::removeStyle); - - if (!checkIfMapWithAIPaintStyleExists()) { - final MapCSSStyleSource style = new MapCSSStyleSource(MapWithAIConfig.getUrls().getMapWithAIPaintStyle(), - MapWithAIPlugin.NAME, "MapWithAI"); - return MapPaintStyles.addStyle(style); - } - return getMapWithAIPaintStyle(); - } - - /** - * Check if the paint style exists - * - * @return {@code true} if the paint style exists - */ - public static synchronized boolean checkIfMapWithAIPaintStyleExists() { - return MapPaintStyles.getStyles().getStyleSources().stream().filter(MapCSSStyleSource.class::isInstance) - .map(MapCSSStyleSource.class::cast) - .anyMatch(source -> MapWithAIConfig.getUrls().getMapWithAIPaintStyle().equals(source.url) - || TEST_PATTERN.matcher(source.url).matches()); - } - - /** - * Remove MapWithAI paint styles - */ - public static synchronized void removeMapWithAIPaintStyles() { - // WebStart has issues with streams and EDT permissions. Don't use streams. - for (StyleSource style : new ArrayList<>(MapPaintStyles.getStyles().getStyleSources())) { - if (MapWithAIConfig.getUrls().getMapWithAIPaintStyle().equals(style.url) - || TEST_PATTERN.matcher(style.url).matches()) { - GuiHelper.runInEDT(() -> MapPaintStyles.removeStyle(style)); - } - } - } - - /** - * Get any MapWithAI paint style - * - * @return get the MapWithAI Paint style - */ - public static synchronized StyleSource getMapWithAIPaintStyle() { - return MapPaintStyles.getStyles().getStyleSources().stream() - .filter(source -> MapWithAIConfig.getUrls().getMapWithAIPaintStyle().equals(source.url) - || TEST_PATTERN.matcher(source.url).matches()) - .findAny().orElse(null); - } - - /** - * Add sources to the paint style - * - * @param ds The dataset to add sources to - */ - public static synchronized void addSourcesToPaintStyle(DataSet ds) { - StyleSource styleSource = addMapWithAIPaintStyles(); - if (styleSource == null) { - return; - } - List sources = ds.allPrimitives().stream().map(MapPaintUtils::getSourceValue).filter(Objects::nonNull) - .map(s -> s.replace('.', '_')).distinct().collect(Collectors.toList()); - if (!styleSource.isLoaded()) { - styleSource.loadStyleSource(); - } - List list = styleSource.settings; - for (StyleSetting setting : list) { - if (setting instanceof StyleSetting.ColorStyleSetting) { - StyleSetting.ColorStyleSetting csetting = (StyleSetting.ColorStyleSetting) setting; - if (csetting.label != null) { - String rLabel = csetting.label.replaceAll("color$", "").trim(); - sources.removeIf(rLabel::equalsIgnoreCase); - } - } - } - Map> groups = styleSource.settingGroups; - String group = groups.keySet().stream().filter(p -> p.key != null && p.key.contains("color")).map(p -> p.key) - .findFirst().orElse(null); - try (CachedFile cachedFile = styleSource.getCachedFile()) { - File file = cachedFile.getFile(); - String path = file.getAbsolutePath(); - for (String prefix : Arrays.asList("file:", "jar:")) { - if (!path.startsWith(prefix)) { - path = prefix.concat(path); - } - } - try (ZipFile zipFile = new ZipFile(file.getAbsolutePath())) { - writeZipData(zipFile, group, sources); - } catch (ZipException e) { - Logging.trace(e); - // Assume that it is a standard file, not a zip file. - try (OutputStream out = Files.newOutputStream(Paths.get(file.getName() + ".tmp")); - BufferedReader bufferedReader = new BufferedReader( - new InputStreamReader(Files.newInputStream(file.toPath()), StandardCharsets.UTF_8))) { - writeData(out, bufferedReader, group, sources); - } - Files.move(new File(file.getName() + ".tmp").toPath(), new File(file.getName()).toPath(), - StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); - } - - styleSource.loadStyleSource(); - } catch (IOException e) { - Logging.error(e); - } - } - - private static void writeZipData(ZipFile file, String group, List sources) throws IOException { - try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(Paths.get(file.getName() + ".tmp")))) { - for (Iterator e = file.stream().iterator(); e.hasNext();) { - ZipEntry current = e.next(); - // For the entry we are overwriting, we cannot use the current zipentry, we must - // make a new one. - if (!current.getName().equalsIgnoreCase(MAPWITHAI_MAPCSS_ZIP_NAME)) { - out.putNextEntry(current); - try (InputStream is = file.getInputStream(current)) { - byte[] buf = new byte[1024]; - int len; - while ((len = is.read(buf)) > 0) { - out.write(buf, 0, len); - } - } - out.closeEntry(); - continue; - } - out.putNextEntry(new ZipEntry(MAPWITHAI_MAPCSS_ZIP_NAME)); - try (BufferedReader bufferedReader = new BufferedReader( - new InputStreamReader(file.getInputStream(current), StandardCharsets.UTF_8))) { - writeData(out, bufferedReader, group, sources); - } - out.closeEntry(); - } - } - Files.move(new File(file.getName() + ".tmp").toPath(), new File(file.getName()).toPath(), - StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); - } - - private static void writeData(OutputStream out, BufferedReader bufferedReader, String group, List sources) - throws IOException { - String line = bufferedReader.readLine(); - while (line != null && !line.contains("End Settings for the paint style")) { - out.write(line.getBytes(StandardCharsets.UTF_8)); - out.write(System.lineSeparator().getBytes(StandardCharsets.UTF_8)); - line = bufferedReader.readLine(); - } - /* Finish writing the comment */ - while (line != null && !line.endsWith("*/")) { - out.write(line.getBytes(StandardCharsets.UTF_8)); - out.write(System.lineSeparator().getBytes(StandardCharsets.UTF_8)); - line = bufferedReader.readLine(); - } - if (line != null) { - out.write(line.getBytes(StandardCharsets.UTF_8)); - out.write(System.lineSeparator().getBytes(StandardCharsets.UTF_8)); - } - - for (String source : sources) { - out.write(System.lineSeparator().getBytes(StandardCharsets.UTF_8)); - String simpleSource = source.replaceAll("[() /${}:,]", "_"); - StringBuilder sb = new StringBuilder(64).append("setting::").append(simpleSource).append("{") - .append("type:color;").append("default:").append(simpleSource) - .append(ColorHelper.color2html(getRandomColor(source))).append(";label:tr(\"{0} color\",\"") - .append(source).append("\");"); - if (group != null) { - sb.append("group:\"").append(group).append("\";"); - } - sb.append('}'); - out.write(sb.toString().getBytes(StandardCharsets.UTF_8)); - out.write(System.lineSeparator().getBytes(StandardCharsets.UTF_8)); - sb = new StringBuilder( - "*[/^(source|mapwithai:source)$/][any(tag(\"source\"), tag(\"mapwithai:source\"))=\"") - .append(source).append("\"]{set_color_programatic:setting(\"").append(simpleSource) - .append("\");}"); - out.write(sb.toString().getBytes(StandardCharsets.UTF_8)); - } - while ((line = bufferedReader.readLine()) != null) { - out.write(line.getBytes(StandardCharsets.UTF_8)); - out.write(System.lineSeparator().getBytes(StandardCharsets.UTF_8)); - } - } - - private static Color getRandomColor(String sourceName) { - if (Stream.of("mapwithai", "maxar", "digitalglobe", "microsoft/") - .anyMatch(i -> sourceName.toLowerCase(Locale.ROOT).contains(i.toLowerCase(Locale.ROOT)))) { - return SafeColors.AI_MAGENTA.getColor(); - } - SafeColors[] colors = Stream.of(SafeColors.values()).filter(c -> SafeColors.AI_MAGENTA != c) - .toArray(SafeColors[]::new); - CRC32 crc = new CRC32(); - crc.update(sourceName.getBytes(StandardCharsets.UTF_8)); - - double bucket = crc.getValue() / CRC_DIVIDE_TO_TEN_K_MAX; - double bucketSize = 10_000d / colors.length; - for (int i = 1; i <= colors.length; i++) { - if (bucket < bucketSize * i) { - return colors[i - 1].getColor(); - } - } - return colors[colors.length - 1].getColor(); - } - - private static String getSourceValue(IPrimitive p) { - if (p.hasTag(SOURCE_KEY)) { - return p.get(SOURCE_KEY); - } - if (p.hasTag(MAPWITHAI_SOURCE_KEY)) { - return p.get(MAPWITHAI_SOURCE_KEY); - } - return null; - } - -} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/MapWithAICopyProhibit.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/MapWithAICopyProhibit.java deleted file mode 100644 index e7f5a388..00000000 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/tools/MapWithAICopyProhibit.java +++ /dev/null @@ -1,68 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.tools; - -import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.Notification; -import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils; -import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData; -import org.openstreetmap.josm.gui.layer.MainLayerManager; -import org.openstreetmap.josm.gui.util.GuiHelper; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAILayer; -import org.openstreetmap.josm.tools.Destroyable; -import org.openstreetmap.josm.tools.Logging; - -import javax.swing.JOptionPane; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.io.IOException; -import java.util.stream.Stream; - -import static org.openstreetmap.josm.gui.help.HelpUtil.ht; -import static org.openstreetmap.josm.tools.I18n.tr; - -/** - * Keep users from copying from the MapWithAI layer to the OSM layer - */ -public class MapWithAICopyProhibit implements MainLayerManager.ActiveLayerChangeListener, Destroyable { - /** - * Create a new listener to keep copy-paste from happening between the MapWithAI - * layer and the OSM layer - */ - public MapWithAICopyProhibit() { - MainApplication.getLayerManager().addActiveLayerChangeListener(this); - } - - @Override - public void activeOrEditLayerChanged(MainLayerManager.ActiveLayerChangeEvent e) { - if (e.getPreviousActiveLayer() instanceof MapWithAILayer && ClipboardUtils.getClipboardContent() != null - && Stream.of(ClipboardUtils.getClipboardContent().getTransferDataFlavors()) - .anyMatch(PrimitiveTransferData.DATA_FLAVOR::equals)) { - PrimitiveTransferData data; - try { - Object tData = ClipboardUtils.getClipboardContent().getTransferData(PrimitiveTransferData.DATA_FLAVOR); - if (tData instanceof PrimitiveTransferData) { - data = (PrimitiveTransferData) tData; - } else { - return; - } - } catch (UnsupportedFlavorException | IOException exception) { - Logging.error(exception); - return; - } - DataSet dataSet = ((MapWithAILayer) e.getPreviousActiveLayer()).getDataSet(); - if (data.getAll().stream().anyMatch(pdata -> dataSet.getPrimitiveById(pdata) != null)) { - ClipboardUtils.clear(); - Notification notification = new Notification(tr( - "Please use the `MapWithAI: Add Selected Data` command instead of copying and pasting from the MapWithAI Layer.")) - .setDuration(Notification.TIME_DEFAULT).setIcon(JOptionPane.INFORMATION_MESSAGE) - .setHelpTopic(ht("Plugin/MapWithAI#BasicUsage")); - GuiHelper.runInEDT(notification::show); - } - } - } - - @Override - public void destroy() { - MainApplication.getLayerManager().removeActiveLayerChangeListener(this); - } -} diff --git a/src/main/resources/images/dialogs/kaart.svg b/src/main/resources/images/dialogs/kaart.svg deleted file mode 100644 index c4ea62a6..00000000 --- a/src/main/resources/images/dialogs/kaart.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/images/dialogs/mapwithai_text.svg b/src/main/resources/images/dialogs/mapwithai_text.svg deleted file mode 100644 index 2dbac368..00000000 --- a/src/main/resources/images/dialogs/mapwithai_text.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - Group 15 - Created with Sketch. - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/images/kaart.svg b/src/main/resources/images/kaart.svg deleted file mode 100644 index c4ea62a6..00000000 --- a/src/main/resources/images/kaart.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/images/mapwithai.svg b/src/main/resources/images/mapwithai.svg deleted file mode 100644 index 7c3be804..00000000 --- a/src/main/resources/images/mapwithai.svg +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - image/svg+xml - - Group 15 - - - - - - - Group 15 - Created with Sketch. - - - - - - - - - - - - - - diff --git a/src/main/resources/images/mapwithai_text.svg b/src/main/resources/images/mapwithai_text.svg deleted file mode 100644 index 2dbac368..00000000 --- a/src/main/resources/images/mapwithai_text.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - Group 15 - Created with Sketch. - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/images/preferences/mapwithai.svg b/src/main/resources/images/preferences/mapwithai.svg deleted file mode 100644 index 7c3be804..00000000 --- a/src/main/resources/images/preferences/mapwithai.svg +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - image/svg+xml - - Group 15 - - - - - - - Group 15 - Created with Sketch. - - - - - - - - - - - - - - diff --git a/src/main/resources/styles/standard/mapwithai.mapcss b/src/main/resources/styles/standard/mapwithai.mapcss deleted file mode 100644 index 9f91501a..00000000 --- a/src/main/resources/styles/standard/mapwithai.mapcss +++ /dev/null @@ -1,245 +0,0 @@ -meta -{ - title: "MapWithAI"; - description: "Visualization of MapWithAI data"; - icon: "mapwithai"; - author: "Taylor Smock"; - version: "1.[[revision]]_[[date]]"; - min-josm-version: "15229"; -} - -/******************************** - * Settings for the paint style * - ********************************/ -@supports (min-josm-version: 15289) { - settings::show_all { - label: tr("Show possible MapWithAI objects"); - } - settings::colors { - label: tr("Source Colors"); - } -} - -setting::show_new { - type: boolean; - label: tr("New"); - default: true; - group: "show_all"; -} -setting::show_modified { - type: boolean; - label: tr("Modified"); - default: false; - group: "show_all"; -} -setting::show_old { - type: boolean; - label: tr("Pre-existing"); - default: false; - group: "show_all"; -} - -setting::decemberHolidayStyle { - type: boolean; - label: tr("Mappy Holidays!"); - default: false; -} - -setting::groupOne { - type: color; - label: tr("Group One color"); - default: groupOne#ff0000; - group: "colors"; -} - -setting::groupTwo { - type: color; - label: tr("Group Two color"); - default: groupTwo#ffa500; - group: "colors"; -} - -setting::groupThree { - type: color; - label: tr("Group Three color"); - default: groupThree#ffd700; - group: "colors"; -} - -setting::groupFour { - type: color; - label: tr("Group Four color"); - default: groupFour#00ff00; - group: "colors"; -} - -setting::groupFive { - type: color; - label: tr("Group Five color"); - default: groupFive#00ffff; - group: "colors"; -} - -setting::groupSix { - type: color; - label: tr("Group Six color"); - default: groupSix#1e90ff; - group: "colors"; -} - -setting::groupSeven { - type: color; - label: tr("Group Seven color"); - default: groupSeven#ffc0cb; - group: "colors"; -} - -setting::groupEight { - type: color; - label: tr("Group Eight color"); - default: groupEight#d3d3d3; - group: "colors"; -} - -setting::groupNine { - type: color; - label: tr("Group Nine color"); - default: groupNine#faf0e6; - group: "colors"; -} - -setting::groupAI { - type: color; - label: tr("Group AI color"); - default: groupAI#ff26d4; - group: "colors"; -} - -/* This requires support in the plugin (will read key for mapwithai.mapcss:boolean:toggle_with_layer) */ -setting::toggle_with_layer { - type: boolean; - label: tr("Toggle paintstyle on/off with layer"); - default: true; -} - -/************************************ - * End Settings for the paint style * - ************************************/ - -/*************************************************** - * Set .mapwithai for all known MapWithAI datasets * - ***************************************************/ - -*[/^(mapwithai:source|source)$/]::mapwithai { - crc: CRC32_checksum(any(tag("source"), tag("mapwithai:source"))) / 429496.7296; -} - -*[prop(crc) < 1111]::mapwithai { - set_color: setting("groupOne"); -} -*[prop(crc) >= 1111][prop(crc) < 2222]::mapwithai { - set_color: setting("groupTwo"); -} -*[prop(crc) >= 2222][prop(crc) < 3333]::mapwithai { - set_color: setting("groupThree"); -} -*[prop(crc) >= 3333][prop(crc) < 4444]::mapwithai { - set_color: setting("groupFour"); -} -*[prop(crc) >= 4444][prop(crc) < 5555]::mapwithai { - set_color: setting("groupFive"); -} -*[prop(crc) >= 5555][prop(crc) < 6666]::mapwithai { - set_color: setting("groupSix"); -} -*[prop(crc) >= 6666][prop(crc) < 7777]::mapwithai { - set_color: setting("groupSeven"); -} -*[prop(crc) >= 7777][prop(crc) < 8888]::mapwithai { - set_color: setting("groupEight"); -} -*[prop(crc) >= 8888][prop(crc) <= 10000]::mapwithai { - set_color: setting("groupNine"); -} - -*[source=~/(microsoft\/.*|digitalglobe|maxar)/].crc::mapwithai { - /* RapiD Magenta */ - set_color: setting("groupAI"); -} - -*[setting("show_new")].set_color:new::mapwithai, -*[setting("show_modified")].set_color:modified::mapwithai, -*[setting("show_old")].set_color!:new!:modified::mapwithai { - casing-width: 8; - casing-color: prop(set_color); - casing-opacity: 0.5; - set .mapwithai; -} - -node|z17-.set_color::mapwithai { - symbol_size: 22; -} -node|z16.set_color::mapwithai { - symbol_size: 12; -} -node|z15.set_color::mapwithai { - symbol_size: 8; -} -node|z14.set_color::mapwithai { - symbol_size: 6; -} -node|z13.set_color::mapwithai { - symbol_size: 4; -} -node|z-12.set_color::mapwithai { - symbol_size: 2; -} - -node[setting("show_new")].set_color:new::mapwithai, -node[setting("show_modified")].set_color:modified::mapwithai, -node[setting("show_old")].set_color!:new!:modified::mapwithai { - symbol-size: prop(symbol_size); - symbol-shape: circle; - symbol-fill-opacity: 0.5; - symbol-fill-color: any(prop(set_color_programatic), prop(set_color)); - z-index: -1; - set .mapwithai; -} - - -way.mapwithai:closed2 { - fill-color: any(prop("set_color_programatic"), prop("set_color")); -} - -way.mapwithai[setting("decemberHolidayStyle")]::mapwithai { - holidayColour: CRC32_checksum(osm_id()) > 2147483647.5 -} - - -/***************** - * Holiday Theme * - *****************/ -way.mapwithai[is_prop_set("holidayColour") && prop("holidayColour")]::mapwithai { - color: mapwithaiDecemberGreen#00FF00; - dashes: 20; - dashes-background-color: mapwithaiDecemberWhite#FFFFFF; -} -way.mapwithai[is_prop_set("holidayColour") && !prop("holidayColour")]::mapwithai { - color: mapwithaiDecemberRed#FC6262; - dashes: 20; - dashes-background-color: mapwithaiDecemberWhite#FFFFFF; -} -way.mapwithai[is_prop_set("holidayColour") && prop("holidayColour")]:closed2::mapwithai { - fill-color: mapwithaiDecemberGreen#00FF00; -} -way.mapwithai[is_prop_set("holidayColour") && !prop("holidayColour")]:closed2::mapwithai { - fill-color: mapwithaiDecemberRed#FC6262; -} - -way.mapwithai[is_prop_set("holidayColour")]:selected::mapwithai { - color: mapWithAIDecemberSelected#FF0000; - dashes: 0; -} -/********************* - * End Holiday Theme * - *********************/ diff --git a/src/main/resources/styles/standard/rapid.mapcss b/src/main/resources/styles/standard/rapid.mapcss deleted file mode 120000 index 4f3ee1fe..00000000 --- a/src/main/resources/styles/standard/rapid.mapcss +++ /dev/null @@ -1 +0,0 @@ -mapwithai.mapcss \ No newline at end of file diff --git a/src/test/.gitignore b/src/test/.gitignore deleted file mode 100644 index d505577e..00000000 --- a/src/test/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/report -/build -/jacoco.exec diff --git a/src/test/data b/src/test/data deleted file mode 120000 index fc478085..00000000 --- a/src/test/data +++ /dev/null @@ -1 +0,0 @@ -resources \ No newline at end of file diff --git a/src/test/integration/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReaderTestIT.java b/src/test/integration/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReaderTestIT.java deleted file mode 100644 index 20566908..00000000 --- a/src/test/integration/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReaderTestIT.java +++ /dev/null @@ -1,47 +0,0 @@ -// License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.mapwithai.io.mapwithai; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.openstreetmap.josm.tools.I18n.tr; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; -import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType; -import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig; -import org.openstreetmap.josm.plugins.mapwithai.testutils.annotations.NoExceptions; -import org.openstreetmap.josm.plugins.mapwithai.testutils.annotations.Wiremock; -import org.openstreetmap.josm.testutils.annotations.BasicPreferences; -import org.openstreetmap.josm.testutils.annotations.Projection; -import org.openstreetmap.josm.testutils.annotations.Territories; - -/** - * Integration test for {@link MapWithAISourceReader} - * - * @author Taylor Smock - */ -@NoExceptions -@BasicPreferences -@Projection -@Territories -@Wiremock -class MapWithAISourceReaderTestIT { - @Test - @Wiremock(false) - void testDefaultSourceIT() throws IOException { - try (MapWithAISourceReader source = new MapWithAISourceReader( - MapWithAIConfig.getUrls().getMapWithAISourcesJson())) { - List infoList = source.parse().orElse(Collections.emptyList()); - assertFalse(infoList.isEmpty(), "There should be viable sources"); - for (MapWithAIType type : Arrays.asList(MapWithAIType.FACEBOOK, MapWithAIType.THIRD_PARTY)) { - assertTrue(infoList.stream().anyMatch(i -> type.equals(i.getSourceType())), - tr("Type {0} should have more than 0 sources", type.getTypeString())); - } - } - } -} diff --git a/src/test/resources/__files/MapWithAIStyle.zip b/src/test/resources/__files/MapWithAIStyle.zip deleted file mode 100644 index 35bad58a..00000000 Binary files a/src/test/resources/__files/MapWithAIStyle.zip and /dev/null differ diff --git a/src/test/resources/__files/josmfile?page=Styles/MapWithAI&zip=1 b/src/test/resources/__files/josmfile?page=Styles/MapWithAI&zip=1 deleted file mode 100644 index 56467972..00000000 Binary files a/src/test/resources/__files/josmfile?page=Styles/MapWithAI&zip=1 and /dev/null differ diff --git a/src/test/resources/mappings/06_nodes-5f086efd-b8fa-4340-b368-cd4fa7626e0b.json b/src/test/resources/mappings/06_nodes-5f086efd-b8fa-4340-b368-cd4fa7626e0b.json deleted file mode 100644 index f8ed8cda..00000000 --- a/src/test/resources/mappings/06_nodes-5f086efd-b8fa-4340-b368-cd4fa7626e0b.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "id" : "5f086efd-b8fa-4340-b368-cd4fa7626e0b", - "name" : "06_nodes", - "request" : { - "url" : "/0.6/nodes?nodes=176232378", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n\n", - "headers" : { - "Date" : "Thu, 05 Dec 2019 14:51:57 GMT", - "Server" : "Apache/2.4.29 (Ubuntu)", - "Strict-Transport-Security" : [ "max-age=31536000; includeSubDomains; preload", "max-age=31536000; includeSubDomains; preload" ], - "Expect-CT" : [ "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"", "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"" ], - "Cache-Control" : "private, max-age=0, must-revalidate", - "Content-Type" : "text/xml; charset=utf-8", - "Keep-Alive" : "timeout=5, max=99", - "Connection" : "Keep-Alive" - } - }, - "uuid" : "5f086efd-b8fa-4340-b368-cd4fa7626e0b", - "persistent" : true, - "insertionIndex" : 9 -} \ No newline at end of file diff --git a/src/test/resources/mappings/06_nodes-7938c30e-bd58-4c8c-a716-49e434123ccb.json b/src/test/resources/mappings/06_nodes-7938c30e-bd58-4c8c-a716-49e434123ccb.json deleted file mode 100644 index a6b32a02..00000000 --- a/src/test/resources/mappings/06_nodes-7938c30e-bd58-4c8c-a716-49e434123ccb.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "id" : "7938c30e-bd58-4c8c-a716-49e434123ccb", - "name" : "06_nodes", - "request" : { - "url" : "/0.6/nodes?nodes=176220609", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n\n", - "headers" : { - "Date" : "Thu, 05 Dec 2019 14:51:57 GMT", - "Server" : "Apache/2.4.29 (Ubuntu)", - "Strict-Transport-Security" : [ "max-age=31536000; includeSubDomains; preload", "max-age=31536000; includeSubDomains; preload" ], - "Expect-CT" : [ "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"", "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"" ], - "Cache-Control" : "private, max-age=0, must-revalidate", - "Content-Type" : "text/xml; charset=utf-8", - "Keep-Alive" : "timeout=5, max=98", - "Connection" : "Keep-Alive" - } - }, - "uuid" : "7938c30e-bd58-4c8c-a716-49e434123ccb", - "persistent" : true, - "insertionIndex" : 10 -} \ No newline at end of file diff --git a/src/test/resources/mappings/06_nodes-98af8a28-e882-4d58-a828-f149ef8b3913.json b/src/test/resources/mappings/06_nodes-98af8a28-e882-4d58-a828-f149ef8b3913.json deleted file mode 100644 index 6caa6c2d..00000000 --- a/src/test/resources/mappings/06_nodes-98af8a28-e882-4d58-a828-f149ef8b3913.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "id" : "98af8a28-e882-4d58-a828-f149ef8b3913", - "name" : "06_nodes", - "request" : { - "url" : "/0.6/nodes?nodes=6151680832", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Date" : "Thu, 05 Dec 2019 14:57:43 GMT", - "Server" : "Apache/2.4.29 (Ubuntu)", - "Strict-Transport-Security" : [ "max-age=31536000; includeSubDomains; preload", "max-age=31536000; includeSubDomains; preload" ], - "Expect-CT" : [ "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"", "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"" ], - "Cache-Control" : "private, max-age=0, must-revalidate", - "Content-Type" : "text/xml; charset=utf-8", - "Keep-Alive" : "timeout=5, max=100", - "Connection" : "Keep-Alive" - } - }, - "uuid" : "98af8a28-e882-4d58-a828-f149ef8b3913", - "persistent" : true, - "insertionIndex" : 13 -} \ No newline at end of file diff --git a/src/test/resources/mappings/06_nodes-aa8912c8-d2fc-4c2d-bf5e-f0192dad2bb4.json b/src/test/resources/mappings/06_nodes-aa8912c8-d2fc-4c2d-bf5e-f0192dad2bb4.json deleted file mode 100644 index 78001605..00000000 --- a/src/test/resources/mappings/06_nodes-aa8912c8-d2fc-4c2d-bf5e-f0192dad2bb4.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "id" : "aa8912c8-d2fc-4c2d-bf5e-f0192dad2bb4", - "name" : "06_nodes", - "request" : { - "url" : "/0.6/nodes?nodes=6146500887", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Date" : "Thu, 05 Dec 2019 14:56:35 GMT", - "Server" : "Apache/2.4.29 (Ubuntu)", - "Strict-Transport-Security" : [ "max-age=31536000; includeSubDomains; preload", "max-age=31536000; includeSubDomains; preload" ], - "Expect-CT" : [ "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"", "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"" ], - "Cache-Control" : "private, max-age=0, must-revalidate", - "Content-Type" : "text/xml; charset=utf-8", - "Keep-Alive" : "timeout=5, max=99", - "Connection" : "Keep-Alive" - } - }, - "uuid" : "aa8912c8-d2fc-4c2d-bf5e-f0192dad2bb4", - "persistent" : true, - "insertionIndex" : 12 -} \ No newline at end of file diff --git a/src/test/resources/mappings/capabilities-cf884424-9817-4f92-a3af-6b5aa2bc4b0e.json b/src/test/resources/mappings/capabilities-cf884424-9817-4f92-a3af-6b5aa2bc4b0e.json deleted file mode 100644 index fb1880aa..00000000 --- a/src/test/resources/mappings/capabilities-cf884424-9817-4f92-a3af-6b5aa2bc4b0e.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "id" : "cf884424-9817-4f92-a3af-6b5aa2bc4b0e", - "name" : "capabilities", - "request" : { - "url" : "/capabilities", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Date" : "Thu, 05 Dec 2019 14:51:56 GMT", - "Server" : "Apache/2.4.29 (Ubuntu)", - "Cache-Control" : "max-age=0, private, must-revalidate", - "Vary" : "Origin,Accept-Encoding", - "X-Permitted-Cross-Domain-Policies" : "none", - "X-XSS-Protection" : "1; mode=block", - "X-Request-Id" : "XekZjNRQE5fxVSCmvZ8wYAAAAMM", - "X-Download-Options" : "noopen", - "X-Runtime" : "0.011898", - "X-Frame-Options" : "sameorigin", - "X-Content-Type-Options" : "nosniff", - "Content-Security-Policy" : "default-src 'self'; child-src 'self'; connect-src 'self' piwik.openstreetmap.org; font-src 'none'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self' data: www.gravatar.com *.wp.com *.tile.openstreetmap.org *.tile.thunderforest.com *.openstreetmap.fr piwik.openstreetmap.org https://openstreetmap-user-avatars.s3.dualstack.eu-west-1.amazonaws.com; manifest-src 'self'; media-src 'none'; object-src 'self'; script-src 'self' piwik.openstreetmap.org; style-src 'self'; worker-src 'none'", - "X-Powered-By" : "Phusion Passenger 6.0.4", - "Strict-Transport-Security" : "max-age=31536000; includeSubDomains; preload", - "Expect-CT" : "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"", - "Upgrade" : "h2", - "ETag" : "W/\"0d683cbc36a97f94c7e1bcc861adc22a-gzip\"", - "Status" : "200 OK", - "Keep-Alive" : "timeout=5, max=100", - "Connection" : "Keep-Alive", - "Content-Type" : "application/xml; charset=utf-8" - } - }, - "uuid" : "cf884424-9817-4f92-a3af-6b5aa2bc4b0e", - "persistent" : true, - "insertionIndex" : 8 -} \ No newline at end of file diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_abilene_tx_buildings_featureserver-cf2fc003-e869-4bff-9c52-16e73cf89b9d.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_abilene_tx_buildings_featureserver-cf2fc003-e869-4bff-9c52-16e73cf89b9d.json deleted file mode 100644 index 8a4db3fc..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_abilene_tx_buildings_featureserver-cf2fc003-e869-4bff-9c52-16e73cf89b9d.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "cf2fc003-e869-4bff-9c52-16e73cf89b9d", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_abilene_tx_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Abilene_TX_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:20:32 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "cf2fc003-e869-4bff-9c52-16e73cf89b9d", - "persistent" : true, - "insertionIndex" : 58 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_alexandria_va_addresses_featureserver-ffce8d3a-c21a-410b-9303-2b59875b76aa.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_alexandria_va_addresses_featureserver-ffce8d3a-c21a-410b-9303-2b59875b76aa.json deleted file mode 100644 index 3dd9c776..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_alexandria_va_addresses_featureserver-ffce8d3a-c21a-410b-9303-2b59875b76aa.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "ffce8d3a-c21a-410b-9303-2b59875b76aa", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_alexandria_va_addresses_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Alexandria_VA_Addresses/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"serviceItemId\" : \"1bd0c546d5cd42ddb21b67a8770f1ab6\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"serviceDescription\" : \"\", \r\n \"hasVersionedData\" : false, \r\n \"supportsDisconnectedEditing\" : false, \r\n \"hasStaticData\" : false, \r\n \"hasSharedDomains\" : false, \r\n \"maxRecordCount\" : 1000, \r\n \"supportedQueryFormats\" : \"JSON\", \r\n \"supportsVCSProjection\" : false, \r\n \"capabilities\" : \"Query\", \r\n \"description\" : \"\", \r\n \"copyrightText\" : \"\", \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }, \r\n \"initialExtent\" : {\r\n \"xmin\" : -77.14924691073395, \r\n \"ymin\" : 38.772782594141539, \r\n \"xmax\" : -77.032025776479543, \r\n \"ymax\" : 38.862181232869133, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"fullExtent\" : {\r\n \"xmin\" : -77.143527926344575, \r\n \"ymin\" : 38.790392356423013, \r\n \"xmax\" : -77.037744760868918, \r\n \"ymax\" : 38.844571470587653, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"allowGeometryUpdates\" : true, \r\n \"units\" : \"esriDecimalDegrees\", \r\n \"supportsAppend\" : true, \r\n \"supportsSharedDomains\" : true, \r\n \"syncEnabled\" : false, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturnDeleteResults\" : true, \r\n \"xssPreventionInfo\" : {\r\n \"xssPreventionEnabled\" : true, \r\n \"xssPreventionRule\" : \"InputOnly\", \r\n \"xssInputRule\" : \"rejectInvalid\"\r\n }, \r\n \"layers\" : [\r\n {\r\n \"id\" : 0, \r\n \"name\" : \"Alexandria_Addresses\", \r\n \"parentLayerId\" : -1, \r\n \"defaultVisibility\" : true, \r\n \"subLayerIds\" : null, \r\n \"minScale\" : 320000, \r\n \"maxScale\" : 0, \r\n \"geometryType\" : \"esriGeometryPoint\"\r\n }\r\n ], \r\n \"tables\" : []\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Tue, 19 May 2020 22:42:39 GMT", - "ETag" : "2052300429", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|f49f6d17a708dc479be4188245742c9f.ac81e339_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_22", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:36:50 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 4b2d2d4c49521bf403522140b78283a1.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "YDZdndZ1tycmd_H71gCopxPSIxVgmCs8YSw3aAEg01XN-_B0x5iglw==" - } - }, - "uuid" : "ffce8d3a-c21a-410b-9303-2b59875b76aa", - "persistent" : true, - "insertionIndex" : 33 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_alexandria_va_addresses_featureserver_0-ee34e070-0bf4-4a36-bfe5-4833a8f25ae5.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_alexandria_va_addresses_featureserver_0-ee34e070-0bf4-4a36-bfe5-4833a8f25ae5.json deleted file mode 100644 index 4b88e2c1..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_alexandria_va_addresses_featureserver_0-ee34e070-0bf4-4a36-bfe5-4833a8f25ae5.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "ee34e070-0bf4-4a36-bfe5-4833a8f25ae5", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_alexandria_va_addresses_featureserver_0", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Alexandria_VA_Addresses/FeatureServer/0", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"id\" : 0, \r\n \"name\" : \"Alexandria_Addresses\", \r\n \"type\" : \"Feature Layer\", \r\n \"serviceItemId\" : \"1bd0c546d5cd42ddb21b67a8770f1ab6\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"displayField\" : \"ADD_TYPE\", \r\n \"description\" : \"This layer package contains the pre-processed address points for the City of Alexandria, Virginia import to OpenStreetMap.\", \r\n \"copyrightText\" : \"City of Alexandria, VA\", \r\n \"defaultVisibility\" : true, \r\n \"editingInfo\" : {\r\n \"lastEditDate\" : 1589928159583\r\n }, \r\n \"relationships\" : [], \r\n \"isDataVersioned\" : false, \r\n \"supportsAppend\" : true, \r\n \"supportsCalculate\" : true, \r\n \"supportsASyncCalculate\" : true, \r\n \"supportsTruncate\" : false, \r\n \"supportsAttachmentsByUploadId\" : true, \r\n \"supportsAttachmentsResizing\" : true, \r\n \"supportsRollbackOnFailureParameter\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsExceedsLimitStatistics\" : true, \r\n \"supportsAdvancedQueries\" : true, \r\n \"supportsValidateSql\" : true, \r\n \"supportsCoordinatesQuantization\" : true, \r\n \"supportsFieldDescriptionProperty\" : true, \r\n \"supportsQuantizationEditMode\" : true, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturningQueryGeometry\" : true, \r\n \"advancedQueryCapabilities\" : {\r\n \"supportsPagination\" : true, \r\n \"supportsPaginationOnAggregatedQueries\" : true, \r\n \"supportsQueryRelatedPagination\" : true, \r\n \"supportsQueryWithDistance\" : true, \r\n \"supportsReturningQueryExtent\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsOrderBy\" : true, \r\n \"supportsDistinct\" : true, \r\n \"supportsQueryWithResultType\" : true, \r\n \"supportsSqlExpression\" : true, \r\n \"supportsAdvancedQueryRelated\" : true, \r\n \"supportsCountDistinct\" : true, \r\n \"supportsPercentileStatistics\" : true, \r\n \"supportsLod\" : true, \r\n \"supportsQueryWithLodSR\" : false, \r\n \"supportedLodTypes\" : [\r\n \"geohash\"\r\n ], \r\n \"supportsReturningGeometryCentroid\" : false, \r\n \"supportsQueryWithDatumTransformation\" : true, \r\n \"supportsHavingClause\" : true, \r\n \"supportsOutFieldSQLExpression\" : true, \r\n \"supportsMaxRecordCountFactor\" : true, \r\n \"supportsTopFeaturesQuery\" : true, \r\n \"supportsDisjointSpatialRel\" : true, \r\n \"supportsQueryWithCacheHint\" : true\r\n }, \r\n \"useStandardizedQueries\" : true, \r\n \"geometryType\" : \"esriGeometryPoint\", \r\n \"minScale\" : 320000, \r\n \"maxScale\" : 0, \r\n \"extent\" : {\r\n \"xmin\" : -77.143527926344575, \r\n \"ymin\" : 38.790392356423013, \r\n \"xmax\" : -77.037744760868918, \r\n \"ymax\" : 38.844571470587653, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"drawingInfo\":{\"renderer\":{\"type\":\"uniqueValue\",\"field1\":\"Import_2_OSM\",\"uniqueValueInfos\":[{\"value\":\"1\",\"symbol\":{\"color\":[56,168,0,255],\"size\":7.5,\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriSMS\",\"style\":\"esriSMSCircle\",\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"}},\"label\":\"1\"},{\"value\":\"2\",\"symbol\":{\"color\":[168,0,0,255],\"size\":7.5,\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriSMS\",\"style\":\"esriSMSCircle\",\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"}},\"label\":\"2\"}]},\"transparency\":0}, \r\n \"allowGeometryUpdates\" : true, \r\n \"hasAttachments\" : false, \r\n \"viewSourceHasAttachments\" : false, \r\n \"htmlPopupType\" : \"esriServerHTMLPopupTypeAsHTMLText\", \r\n \"hasMetadata\" : true, \r\n \"hasM\" : false, \r\n \"hasZ\" : false, \r\n \"objectIdField\" : \"OBJECTID\", \r\n \"uniqueIdField\" : \r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"isSystemMaintained\" : true\r\n }, \r\n \"globalIdField\" : \"\", \r\n \"typeIdField\" : \"Import_2_OSM\", \r\n \"fields\" : [\r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"type\" : \"esriFieldTypeOID\", \r\n \"alias\" : \"OBJECTID\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : false, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"ADD_TYPE\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"ADD_TYPE\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 2, \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"ADDR_RECNO\", \r\n \"type\" : \"esriFieldTypeInteger\", \r\n \"alias\" : \"ADDR_RECNO\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_housenumber\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:housenumber\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_street\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:street\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_unit\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:unit\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_city\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:city\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 20, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_state\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:state\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 2, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_postcode\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:postcode\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Import_2_OSM\", \r\n \"type\" : \"esriFieldTypeSmallInteger\", \r\n \"alias\" : \"Import_2_OSM\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BlockGroup\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"BlockGroup\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 12, \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }\r\n ], \r\n \"indexes\" : [\r\n {\r\n \"name\" : \"PK__ALEXANDR__F4B70D855511EA09\", \r\n \"fields\" : \"OBJECTID\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : true, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"user_58277.ALEXANDRIA_ADDRESSES_ALEXANDRIA_ADDRESSES_Shape_sidx\", \r\n \"fields\" : \"Shape\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }\r\n ], \r\n \"types\" : [\r\n {\r\n \"id\" : 1, \r\n \"name\" : \"1\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"1\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 1, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }, \r\n {\r\n \"id\" : 2, \r\n \"name\" : \"2\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"2\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 2, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }\r\n ], \r\n \"templates\" : [], \r\n \"supportedQueryFormats\" : \"JSON, geoJSON, PBF\", \r\n \"hasStaticData\" : false, \r\n \"maxRecordCount\" : 2000, \r\n \"standardMaxRecordCount\" : 32000, \r\n \"standardMaxRecordCountNoGeometry\" : 32000, \r\n \"tileMaxRecordCount\" : 8000, \r\n \"maxRecordCountFactor\" : 1, \r\n \"capabilities\" : \"Query\"\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Tue, 19 May 2020 22:42:39 GMT", - "ETag" : "-1724016979", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|2ab314c3196cc849864939a0fc640d40.c783cdb2_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_16", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:51:29 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 045f1e1f031241f3808c557a2b5d6b0a.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "KGM_uMAg_7pNcF5dTcDsvys231JZHZxM-CpLOtD1lOqV3lz9Gw9Pwg==" - } - }, - "uuid" : "ee34e070-0bf4-4a36-bfe5-4833a8f25ae5", - "persistent" : true, - "insertionIndex" : 35 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_baton_rouge_la_buildings_featureserver-dd6fd63f-5bf7-44e1-96cb-3accb0d9e652.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_baton_rouge_la_buildings_featureserver-dd6fd63f-5bf7-44e1-96cb-3accb0d9e652.json deleted file mode 100644 index 04fbbfc6..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_baton_rouge_la_buildings_featureserver-dd6fd63f-5bf7-44e1-96cb-3accb0d9e652.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "dd6fd63f-5bf7-44e1-96cb-3accb0d9e652", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_baton_rouge_la_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Baton_Rouge_LA_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:20:21 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "dd6fd63f-5bf7-44e1-96cb-3accb0d9e652", - "persistent" : true, - "insertionIndex" : 57 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_boston_ma_addresses_featureserver-1878a845-e581-4519-9568-57e3c4ca5681.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_boston_ma_addresses_featureserver-1878a845-e581-4519-9568-57e3c4ca5681.json deleted file mode 100644 index d0007ced..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_boston_ma_addresses_featureserver-1878a845-e581-4519-9568-57e3c4ca5681.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "1878a845-e581-4519-9568-57e3c4ca5681", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_boston_ma_addresses_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Boston_MA_Addresses/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:05:04 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "1878a845-e581-4519-9568-57e3c4ca5681", - "persistent" : true, - "insertionIndex" : 47 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_flagstaff_az_addresses_featureserver-f5b30af0-0177-459b-ac7a-b4fc0be7d99a.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_flagstaff_az_addresses_featureserver-f5b30af0-0177-459b-ac7a-b4fc0be7d99a.json deleted file mode 100644 index fe34f434..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_flagstaff_az_addresses_featureserver-f5b30af0-0177-459b-ac7a-b4fc0be7d99a.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "f5b30af0-0177-459b-ac7a-b4fc0be7d99a", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_flagstaff_az_addresses_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Flagstaff_AZ_Addresses/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"serviceItemId\" : \"d3661b8ae03947899efcf7d1b1e848d3\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"serviceDescription\" : \"\", \r\n \"hasVersionedData\" : false, \r\n \"supportsDisconnectedEditing\" : false, \r\n \"hasStaticData\" : false, \r\n \"hasSharedDomains\" : false, \r\n \"maxRecordCount\" : 1000, \r\n \"supportedQueryFormats\" : \"JSON\", \r\n \"supportsVCSProjection\" : false, \r\n \"capabilities\" : \"Query\", \r\n \"description\" : \"\", \r\n \"copyrightText\" : \"\", \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }, \r\n \"initialExtent\" : {\r\n \"xmin\" : -111.7006323013569, \r\n \"ymin\" : 35.114828043300058, \r\n \"xmax\" : -111.51533371464302, \r\n \"ymax\" : 35.256145916700071, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"fullExtent\" : {\r\n \"xmin\" : -111.70981941999996, \r\n \"ymin\" : 35.127538299000037, \r\n \"xmax\" : -111.53245605399997, \r\n \"ymax\" : 35.239775444000031, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"allowGeometryUpdates\" : true, \r\n \"units\" : \"esriDecimalDegrees\", \r\n \"supportsAppend\" : true, \r\n \"supportsSharedDomains\" : true, \r\n \"syncEnabled\" : false, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturnDeleteResults\" : true, \r\n \"xssPreventionInfo\" : {\r\n \"xssPreventionEnabled\" : true, \r\n \"xssPreventionRule\" : \"InputOnly\", \r\n \"xssInputRule\" : \"rejectInvalid\"\r\n }, \r\n \"layers\" : [\r\n {\r\n \"id\" : 0, \r\n \"name\" : \"Flagstaff_addresses\", \r\n \"parentLayerId\" : -1, \r\n \"defaultVisibility\" : true, \r\n \"subLayerIds\" : null, \r\n \"minScale\" : 0, \r\n \"maxScale\" : 0, \r\n \"geometryType\" : \"esriGeometryPoint\"\r\n }\r\n ], \r\n \"tables\" : []\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Fri, 24 Apr 2020 15:51:21 GMT", - "ETag" : "2031666911", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|2c370141abd248428ef6e69b293d9f11.c783cf6e_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_16", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:36:09 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 4b2d2d4c49521bf403522140b78283a1.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "ZRSFCywyLoG4IUD_WVCx4FsBIwhcBiykal-zBL-mTe7_PenanHk9Og==" - } - }, - "uuid" : "f5b30af0-0177-459b-ac7a-b4fc0be7d99a", - "persistent" : true, - "insertionIndex" : 23 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_flagstaff_az_addresses_featureserver_0-d0e9831e-6eff-4f7e-83ee-f09bad38c40d.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_flagstaff_az_addresses_featureserver_0-d0e9831e-6eff-4f7e-83ee-f09bad38c40d.json deleted file mode 100644 index 6b6af2aa..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_flagstaff_az_addresses_featureserver_0-d0e9831e-6eff-4f7e-83ee-f09bad38c40d.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "d0e9831e-6eff-4f7e-83ee-f09bad38c40d", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_flagstaff_az_addresses_featureserver_0", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Flagstaff_AZ_Addresses/FeatureServer/0", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"id\" : 0, \r\n \"name\" : \"Flagstaff_addresses\", \r\n \"type\" : \"Feature Layer\", \r\n \"serviceItemId\" : \"d3661b8ae03947899efcf7d1b1e848d3\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"displayField\" : \"BlockGroup\", \r\n \"description\" : \"This layer contains the preprocessed address points for the City of Flagstadd OSM import.\", \r\n \"copyrightText\" : \"Public domain\", \r\n \"defaultVisibility\" : true, \r\n \"editingInfo\" : {\r\n \"lastEditDate\" : 1587743481856\r\n }, \r\n \"relationships\" : [], \r\n \"isDataVersioned\" : false, \r\n \"supportsAppend\" : true, \r\n \"supportsCalculate\" : true, \r\n \"supportsASyncCalculate\" : true, \r\n \"supportsTruncate\" : false, \r\n \"supportsAttachmentsByUploadId\" : true, \r\n \"supportsAttachmentsResizing\" : true, \r\n \"supportsRollbackOnFailureParameter\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsExceedsLimitStatistics\" : true, \r\n \"supportsAdvancedQueries\" : true, \r\n \"supportsValidateSql\" : true, \r\n \"supportsCoordinatesQuantization\" : true, \r\n \"supportsFieldDescriptionProperty\" : true, \r\n \"supportsQuantizationEditMode\" : true, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturningQueryGeometry\" : true, \r\n \"advancedQueryCapabilities\" : {\r\n \"supportsPagination\" : true, \r\n \"supportsPaginationOnAggregatedQueries\" : true, \r\n \"supportsQueryRelatedPagination\" : true, \r\n \"supportsQueryWithDistance\" : true, \r\n \"supportsReturningQueryExtent\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsOrderBy\" : true, \r\n \"supportsDistinct\" : true, \r\n \"supportsQueryWithResultType\" : true, \r\n \"supportsSqlExpression\" : true, \r\n \"supportsAdvancedQueryRelated\" : true, \r\n \"supportsCountDistinct\" : true, \r\n \"supportsPercentileStatistics\" : true, \r\n \"supportsLod\" : true, \r\n \"supportsQueryWithLodSR\" : false, \r\n \"supportedLodTypes\" : [\r\n \"geohash\"\r\n ], \r\n \"supportsReturningGeometryCentroid\" : false, \r\n \"supportsQueryWithDatumTransformation\" : true, \r\n \"supportsHavingClause\" : true, \r\n \"supportsOutFieldSQLExpression\" : true, \r\n \"supportsMaxRecordCountFactor\" : true, \r\n \"supportsTopFeaturesQuery\" : true, \r\n \"supportsDisjointSpatialRel\" : true, \r\n \"supportsQueryWithCacheHint\" : true\r\n }, \r\n \"useStandardizedQueries\" : true, \r\n \"geometryType\" : \"esriGeometryPoint\", \r\n \"minScale\" : 0, \r\n \"maxScale\" : 0, \r\n \"extent\" : {\r\n \"xmin\" : -111.70981941999996, \r\n \"ymin\" : 35.127538299000037, \r\n \"xmax\" : -111.53245605399997, \r\n \"ymax\" : 35.239775444000031, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"drawingInfo\":{\"renderer\":{\"type\":\"uniqueValue\",\"field1\":\"Import_2_OSM\",\"uniqueValueInfos\":[{\"value\":\"1\",\"symbol\":{\"color\":[56,168,0,255],\"size\":7.5,\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriSMS\",\"style\":\"esriSMSCircle\",\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"}},\"label\":\"Import (No Match)\"},{\"value\":\"0\",\"symbol\":{\"color\":[255,85,0,255],\"size\":7.5,\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriSMS\",\"style\":\"esriSMSCircle\",\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"}},\"label\":\"No Import (Matched Building)\"},{\"value\":\"2\",\"symbol\":{\"color\":[255,0,0,255],\"size\":7.5,\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriSMS\",\"style\":\"esriSMSCircle\",\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"}},\"label\":\"No Import (Matched Point)\"}]},\"transparency\":0}, \r\n \"allowGeometryUpdates\" : true, \r\n \"hasAttachments\" : false, \r\n \"viewSourceHasAttachments\" : false, \r\n \"htmlPopupType\" : \"esriServerHTMLPopupTypeAsHTMLText\", \r\n \"hasMetadata\" : true, \r\n \"hasM\" : false, \r\n \"hasZ\" : false, \r\n \"objectIdField\" : \"OBJECTID\", \r\n \"uniqueIdField\" : \r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"isSystemMaintained\" : true\r\n }, \r\n \"globalIdField\" : \"\", \r\n \"typeIdField\" : \"Import_2_OSM\", \r\n \"fields\" : [\r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"type\" : \"esriFieldTypeOID\", \r\n \"alias\" : \"OBJECTID\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : false, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Import_2_OSM\", \r\n \"type\" : \"esriFieldTypeSmallInteger\", \r\n \"alias\" : \"Import_2_OSM\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BlockGroup\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"Block Group\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 12, \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_housenumber\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:housenumber\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_street\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:street\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_unit\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:unit\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_city\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:city\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 20, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_state\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:state\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 2, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_postcode\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:postcode\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }\r\n ], \r\n \"indexes\" : [\r\n {\r\n \"name\" : \"PK__FLAGSTAF__F4B70D85FFC95E6A\", \r\n \"fields\" : \"OBJECTID\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : true, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"user_58277.FLAGSTAFF_ADDRESSES_FLAGSTAFF_ADDRESSES_Shape_sidx\", \r\n \"fields\" : \"Shape\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }\r\n ], \r\n \"types\" : [\r\n {\r\n \"id\" : 1, \r\n \"name\" : \"1\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"1\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 1, \r\n \"BlockGroup\" : null, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }, \r\n {\r\n \"id\" : 0, \r\n \"name\" : \"0\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"0\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 0, \r\n \"BlockGroup\" : null, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }, \r\n {\r\n \"id\" : 2, \r\n \"name\" : \"2\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"2\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 2, \r\n \"BlockGroup\" : null, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }\r\n ], \r\n \"templates\" : [], \r\n \"supportedQueryFormats\" : \"JSON, geoJSON, PBF\", \r\n \"hasStaticData\" : false, \r\n \"maxRecordCount\" : 2000, \r\n \"standardMaxRecordCount\" : 32000, \r\n \"standardMaxRecordCountNoGeometry\" : 32000, \r\n \"tileMaxRecordCount\" : 8000, \r\n \"maxRecordCountFactor\" : 1, \r\n \"capabilities\" : \"Query\"\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Fri, 24 Apr 2020 15:51:21 GMT", - "ETag" : "-209216560", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|7e8d62191c3553479d225c0a3379b8e3.20755cc0_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_28", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:53:42 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 23241b9c368643949e3bb1a1ba4e97c3.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "MHGQk6i1RCFrecmfpmYYqd3X0UjOEsa5CRDKClvLG5AKjuD6AX6aYA==" - } - }, - "uuid" : "d0e9831e-6eff-4f7e-83ee-f09bad38c40d", - "persistent" : true, - "insertionIndex" : 40 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_fort_collins_co_buildings_featureserver-5b042812-8b74-43cc-8f3c-5455209e6174.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_fort_collins_co_buildings_featureserver-5b042812-8b74-43cc-8f3c-5455209e6174.json deleted file mode 100644 index 83214840..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_fort_collins_co_buildings_featureserver-5b042812-8b74-43cc-8f3c-5455209e6174.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "5b042812-8b74-43cc-8f3c-5455209e6174", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_fort_collins_co_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Fort_Collins_CO_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:20:08 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "5b042812-8b74-43cc-8f3c-5455209e6174", - "persistent" : true, - "insertionIndex" : 56 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_franklin_county_oh_buildings_featureserver-9f5608b0-7f9b-4744-9bfa-aca77b68a338.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_franklin_county_oh_buildings_featureserver-9f5608b0-7f9b-4744-9bfa-aca77b68a338.json deleted file mode 100644 index dbacf840..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_franklin_county_oh_buildings_featureserver-9f5608b0-7f9b-4744-9bfa-aca77b68a338.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "9f5608b0-7f9b-4744-9bfa-aca77b68a338", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_franklin_county_oh_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Franklin_County_OH_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:05:57 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "9f5608b0-7f9b-4744-9bfa-aca77b68a338", - "persistent" : true, - "insertionIndex" : 49 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_franklincounty_buildings_featureserver-4a594e6d-4b81-42ad-b547-39005f2a5f62.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_franklincounty_buildings_featureserver-4a594e6d-4b81-42ad-b547-39005f2a5f62.json deleted file mode 100644 index a25355f1..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_franklincounty_buildings_featureserver-4a594e6d-4b81-42ad-b547-39005f2a5f62.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "4a594e6d-4b81-42ad-b547-39005f2a5f62", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_franklincounty_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/FranklinCounty_buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"serviceItemId\" : \"27796b2f1f10470d87c036d2b40c8d64\", \r\n \"serviceDescription\" : \"This layer contains the pre-processed building polygons for Franklin County, Ohio to be added to OpenStreetMap.\", \r\n \"hasVersionedData\" : false, \r\n \"supportsDisconnectedEditing\" : false, \r\n \"hasStaticData\" : false, \r\n \"hasSharedDomains\" : false, \r\n \"maxRecordCount\" : 2000, \r\n \"supportedQueryFormats\" : \"JSON\", \r\n \"supportsVCSProjection\" : false, \r\n \"capabilities\" : \"Query\", \r\n \"description\" : \"\\u003cDIV STYLE=\\\"text-align:Left;font-size:12pt\\\"\\u003e\\u003cDIV\\u003e\\u003cP\\u003e\\u003cSPAN\\u003eThis layer contains the pre-processed building polygons for Franklin County, Ohio to be added to OpenStreetMap.\\u003c/SPAN\\u003e\\u003c/P\\u003e\\u003c/DIV\\u003e\\u003c/DIV\\u003e\", \r\n \"copyrightText\" : \"Franklin County, OH\", \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }, \r\n \"initialExtent\" : {\r\n \"xmin\" : -83.282235450462764, \r\n \"ymin\" : 39.760494183742104, \r\n \"xmax\" : -82.736029000784669, \r\n \"ymax\" : 40.177058256219688, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"fullExtent\" : {\r\n \"xmin\" : -83.257290901561248, \r\n \"ymin\" : 39.794679562712304, \r\n \"xmax\" : -82.76097354968617, \r\n \"ymax\" : 40.142872877249488, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"allowGeometryUpdates\" : true, \r\n \"units\" : \"esriDecimalDegrees\", \r\n \"supportsAppend\" : true, \r\n \"supportsSharedDomains\" : true, \r\n \"size\" : 411803648, \r\n \"syncEnabled\" : false, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturnDeleteResults\" : true, \r\n \"editorTrackingInfo\" : {\r\n \"enableEditorTracking\" : false, \r\n \"enableOwnershipAccessControl\" : false, \r\n \"allowOthersToQuery\" : true, \r\n \"allowOthersToUpdate\" : true, \r\n \"allowOthersToDelete\" : false, \r\n \"allowAnonymousToQuery\" : true, \r\n \"allowAnonymousToUpdate\" : true, \r\n \"allowAnonymousToDelete\" : true\r\n }, \r\n \"xssPreventionInfo\" : {\r\n \"xssPreventionEnabled\" : true, \r\n \"xssPreventionRule\" : \"InputOnly\", \r\n \"xssInputRule\" : \"rejectInvalid\"\r\n }, \r\n \"layers\" : [\r\n {\r\n \"id\" : 0, \r\n \"name\" : \"Franklin_County_Buildings\", \r\n \"parentLayerId\" : -1, \r\n \"defaultVisibility\" : true, \r\n \"subLayerIds\" : null, \r\n \"minScale\" : 0, \r\n \"maxScale\" : 0, \r\n \"geometryType\" : \"esriGeometryPolygon\"\r\n }\r\n ], \r\n \"tables\" : []\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Thu, 28 May 2020 02:39:51 GMT", - "ETag" : "998550336", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|3569b54d95eb004fb3f57de37946c2eb.c85f6638_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_26", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:36:45 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 4b2d2d4c49521bf403522140b78283a1.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "6_UOfvhEidG8Ax57VnxTVjtDf7YJ8Z8Vv-yR1JLe2bzB2eG_LZhDFQ==" - } - }, - "uuid" : "4a594e6d-4b81-42ad-b547-39005f2a5f62", - "persistent" : true, - "insertionIndex" : 31 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_franklincounty_buildings_featureserver_0-8cd10b04-891d-4d27-9282-82e7349f5bd3.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_franklincounty_buildings_featureserver_0-8cd10b04-891d-4d27-9282-82e7349f5bd3.json deleted file mode 100644 index abad4100..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_franklincounty_buildings_featureserver_0-8cd10b04-891d-4d27-9282-82e7349f5bd3.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "8cd10b04-891d-4d27-9282-82e7349f5bd3", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_franklincounty_buildings_featureserver_0", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/FranklinCounty_buildings/FeatureServer/0", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"id\" : 0, \r\n \"name\" : \"Franklin_County_Buildings\", \r\n \"type\" : \"Feature Layer\", \r\n \"serviceItemId\" : \"27796b2f1f10470d87c036d2b40c8d64\", \r\n \"displayField\" : \"name\", \r\n \"description\" : \"This layer contains the pre-processed building polygons for the Franklin County, Ohio import to OpenStreetMap.\", \r\n \"copyrightText\" : \"Franklin County, OH\", \r\n \"defaultVisibility\" : true, \r\n \"editingInfo\" : {\r\n \"lastEditDate\" : 1590633591561\r\n }, \r\n \"relationships\" : [], \r\n \"isDataVersioned\" : false, \r\n \"supportsAppend\" : true, \r\n \"supportsCalculate\" : true, \r\n \"supportsASyncCalculate\" : true, \r\n \"supportsTruncate\" : true, \r\n \"supportsAttachmentsByUploadId\" : true, \r\n \"supportsAttachmentsResizing\" : true, \r\n \"supportsRollbackOnFailureParameter\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsExceedsLimitStatistics\" : true, \r\n \"supportsAdvancedQueries\" : true, \r\n \"supportsValidateSql\" : true, \r\n \"supportsCoordinatesQuantization\" : true, \r\n \"supportsFieldDescriptionProperty\" : true, \r\n \"supportsQuantizationEditMode\" : true, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsMultiScaleGeometry\" : true, \r\n \"supportsReturningQueryGeometry\" : true, \r\n \"hasGeometryProperties\" : true, \r\n \"geometryProperties\" : \r\n {\r\n \"shapeAreaFieldName\" : \"Shape__Area\", \r\n \"shapeLengthFieldName\" : \"Shape__Length\", \r\n \"units\" : \"esriDecimalDegrees\"\r\n }, \r\n \"advancedQueryCapabilities\" : {\r\n \"supportsPagination\" : true, \r\n \"supportsPaginationOnAggregatedQueries\" : true, \r\n \"supportsQueryRelatedPagination\" : true, \r\n \"supportsQueryWithDistance\" : true, \r\n \"supportsReturningQueryExtent\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsOrderBy\" : true, \r\n \"supportsDistinct\" : true, \r\n \"supportsQueryWithResultType\" : true, \r\n \"supportsSqlExpression\" : true, \r\n \"supportsAdvancedQueryRelated\" : true, \r\n \"supportsCountDistinct\" : true, \r\n \"supportsPercentileStatistics\" : true, \r\n \"supportsLod\" : true, \r\n \"supportsQueryWithLodSR\" : false, \r\n \"supportedLodTypes\" : [\r\n \"geohash\"\r\n ], \r\n \"supportsReturningGeometryCentroid\" : true, \r\n \"supportsReturningGeometryProperties\" : true, \r\n \"supportsQueryWithDatumTransformation\" : true, \r\n \"supportsHavingClause\" : true, \r\n \"supportsOutFieldSQLExpression\" : true, \r\n \"supportsMaxRecordCountFactor\" : true, \r\n \"supportsTopFeaturesQuery\" : true, \r\n \"supportsDisjointSpatialRel\" : true, \r\n \"supportsQueryWithCacheHint\" : true\r\n }, \r\n \"useStandardizedQueries\" : true, \r\n \"geometryType\" : \"esriGeometryPolygon\", \r\n \"minScale\" : 0, \r\n \"maxScale\" : 0, \r\n \"extent\" : {\r\n \"xmin\" : -83.257290901561248, \r\n \"ymin\" : 39.794679562712304, \r\n \"xmax\" : -82.76097354968617, \r\n \"ymax\" : 40.142872877249488, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"drawingInfo\":{\"renderer\":{\"type\":\"uniqueValue\",\"field1\":\"Import_2_OSM\",\"uniqueValueInfos\":[{\"value\":\"1\",\"symbol\":{\"color\":[56,168,0,255],\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"},\"type\":\"esriSFS\",\"style\":\"esriSFSSolid\"},\"label\":\"Import\"},{\"value\":\"0\",\"symbol\":{\"color\":[168,0,0,255],\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"},\"type\":\"esriSFS\",\"style\":\"esriSFSSolid\"},\"label\":\"No Import\"}]},\"transparency\":20}, \r\n \"allowGeometryUpdates\" : true, \r\n \"hasAttachments\" : false, \r\n \"htmlPopupType\" : \"esriServerHTMLPopupTypeAsHTMLText\", \r\n \"hasMetadata\" : true, \r\n \"hasM\" : false, \r\n \"hasZ\" : false, \r\n \"objectIdField\" : \"OBJECTID\", \r\n \"uniqueIdField\" : \r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"isSystemMaintained\" : true\r\n }, \r\n \"globalIdField\" : \"\", \r\n \"typeIdField\" : \"Import_2_OSM\", \r\n \"fields\" : [\r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"type\" : \"esriFieldTypeOID\", \r\n \"alias\" : \"OBJECTID\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : false, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BLDG_RECNO\", \r\n \"type\" : \"esriFieldTypeInteger\", \r\n \"alias\" : \"BLDG_RECNO\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"building\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"building\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"name\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"name\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 120, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Import_2_OSM\", \r\n \"type\" : \"esriFieldTypeSmallInteger\", \r\n \"alias\" : \"Import_2_OSM\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BlockGroup\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"BlockGroup\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 12, \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"leisure\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"leisure\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Shape__Area\", \r\n \"type\" : \"esriFieldTypeDouble\", \r\n \"alias\" : \"Shape__Area\", \r\n \"sqlType\" : \"sqlTypeDouble\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Shape__Length\", \r\n \"type\" : \"esriFieldTypeDouble\", \r\n \"alias\" : \"Shape__Length\", \r\n \"sqlType\" : \"sqlTypeDouble\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }\r\n ], \r\n \"indexes\" : [\r\n {\r\n \"name\" : \"PK__FRANKLIN__F4B70D85C036E001\", \r\n \"fields\" : \"OBJECTID\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : true, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"user_58277.FRANKLINCOUNTY_BUILDINGS_FRANKLINCOUNTY_BUILDINGS_Shape_sidx\", \r\n \"fields\" : \"Shape\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"Shape__Area_Index\", \r\n \"fields\" : \"Shape__Area\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"Shape__Length_Index\", \r\n \"fields\" : \"Shape__Length\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"Import_2_OSM_Index\", \r\n \"fields\" : \"Import_2_OSM\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"Import_2_OSM_Index\"\r\n }\r\n ], \r\n \"types\" : [\r\n {\r\n \"id\" : 1, \r\n \"name\" : \"1\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"1\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 1, \r\n \"building\" : null, \r\n \"name\" : null, \r\n \"leisure\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }, \r\n {\r\n \"id\" : 0, \r\n \"name\" : \"0\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"0\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 0, \r\n \"building\" : null, \r\n \"name\" : null, \r\n \"leisure\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }\r\n ], \r\n \"templates\" : [], \r\n \"supportedQueryFormats\" : \"JSON, geoJSON, PBF\", \r\n \"hasStaticData\" : false, \r\n \"maxRecordCount\" : 2000, \r\n \"standardMaxRecordCount\" : 4000, \r\n \"standardMaxRecordCountNoGeometry\" : 32000, \r\n \"tileMaxRecordCount\" : 4000, \r\n \"maxRecordCountFactor\" : 1, \r\n \"capabilities\" : \"Query\"\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Thu, 28 May 2020 02:39:51 GMT", - "ETag" : "-1531747623", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|a6b75f7560fc9243999cefa370206333.f33a29e6_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_23", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:51:38 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 045f1e1f031241f3808c557a2b5d6b0a.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "wahjbddVKfpq3nITQKGOOe3-F4tjXw8gOW8KiWWVXohBUvJlJo78mw==" - } - }, - "uuid" : "8cd10b04-891d-4d27-9282-82e7349f5bd3", - "persistent" : true, - "insertionIndex" : 36 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_indianapolis_in_buildings_featureserver-0839729e-481d-474b-b6c4-186632418ff8.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_indianapolis_in_buildings_featureserver-0839729e-481d-474b-b6c4-186632418ff8.json deleted file mode 100644 index 01cfef6e..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_indianapolis_in_buildings_featureserver-0839729e-481d-474b-b6c4-186632418ff8.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "0839729e-481d-474b-b6c4-186632418ff8", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_indianapolis_in_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Indianapolis_IN_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:20:42 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "0839729e-481d-474b-b6c4-186632418ff8", - "persistent" : true, - "insertionIndex" : 59 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_johns_creek_ga_addresses_featureserver-1af1902a-7f3e-406c-baae-c87beb5f51b2.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_johns_creek_ga_addresses_featureserver-1af1902a-7f3e-406c-baae-c87beb5f51b2.json deleted file mode 100644 index 5ec8cbe2..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_johns_creek_ga_addresses_featureserver-1af1902a-7f3e-406c-baae-c87beb5f51b2.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "id" : "1af1902a-7f3e-406c-baae-c87beb5f51b2", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_johns_creek_ga_addresses_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Johns_Creek_GA_Addresses/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:21:58 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "1af1902a-7f3e-406c-baae-c87beb5f51b2", - "persistent" : true, - "scenarioName" : "scenario-2-Do88DoK2xjTUCXd1-arcgis-rest-services-Johns_Creek_GA_Addresses-FeatureServer", - "requiredScenarioState" : "Started", - "insertionIndex" : 62 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_johns_creek_ga_buildings_featureserver-1f76a677-dad5-47c2-86f9-a6215bd47f09.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_johns_creek_ga_buildings_featureserver-1f76a677-dad5-47c2-86f9-a6215bd47f09.json deleted file mode 100644 index 99afc894..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_johns_creek_ga_buildings_featureserver-1f76a677-dad5-47c2-86f9-a6215bd47f09.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "id" : "1f76a677-dad5-47c2-86f9-a6215bd47f09", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_johns_creek_ga_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Johns_Creek_GA_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:21:50 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "1f76a677-dad5-47c2-86f9-a6215bd47f09", - "persistent" : true, - "scenarioName" : "scenario-1-Do88DoK2xjTUCXd1-arcgis-rest-services-Johns_Creek_GA_Buildings-FeatureServer", - "requiredScenarioState" : "Started", - "insertionIndex" : 61 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_madison_county_ky_addresses_featureserver-66ef9aca-060f-4595-ba9d-426efca7992a.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_madison_county_ky_addresses_featureserver-66ef9aca-060f-4595-ba9d-426efca7992a.json deleted file mode 100644 index 05b1197c..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_madison_county_ky_addresses_featureserver-66ef9aca-060f-4595-ba9d-426efca7992a.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "66ef9aca-060f-4595-ba9d-426efca7992a", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_madison_county_ky_addresses_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Madison_County_KY_Addresses/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:06:32 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "66ef9aca-060f-4595-ba9d-426efca7992a", - "persistent" : true, - "insertionIndex" : 51 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_featureserver-757edf0f-0dc4-4b42-869e-1bc2cf3ecf31.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_featureserver-757edf0f-0dc4-4b42-869e-1bc2cf3ecf31.json deleted file mode 100644 index 5e87dcec..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_featureserver-757edf0f-0dc4-4b42-869e-1bc2cf3ecf31.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "757edf0f-0dc4-4b42-869e-1bc2cf3ecf31", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Orange_County_CA_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"serviceItemId\" : \"9ba701980ab94d8db87524ac6911f862\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"serviceDescription\" : \"\", \r\n \"hasVersionedData\" : false, \r\n \"supportsDisconnectedEditing\" : false, \r\n \"hasStaticData\" : false, \r\n \"hasSharedDomains\" : false, \r\n \"maxRecordCount\" : 1000, \r\n \"supportedQueryFormats\" : \"JSON\", \r\n \"supportsVCSProjection\" : false, \r\n \"capabilities\" : \"Query\", \r\n \"description\" : \"\", \r\n \"copyrightText\" : \"\", \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }, \r\n \"initialExtent\" : {\r\n \"xmin\" : -117.88211576072339, \r\n \"ymin\" : 33.735592108734167, \r\n \"xmax\" : -117.88030887680031, \r\n \"ymax\" : 33.737522189288356, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"fullExtent\" : {\r\n \"xmin\" : -118.12928314353422, \r\n \"ymin\" : 33.381942817168351, \r\n \"xmax\" : -117.39614201190716, \r\n \"ymax\" : 33.9621048644816, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"allowGeometryUpdates\" : true, \r\n \"units\" : \"esriDecimalDegrees\", \r\n \"supportsAppend\" : true, \r\n \"supportsSharedDomains\" : true, \r\n \"syncEnabled\" : false, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturnDeleteResults\" : true, \r\n \"xssPreventionInfo\" : {\r\n \"xssPreventionEnabled\" : true, \r\n \"xssPreventionRule\" : \"InputOnly\", \r\n \"xssInputRule\" : \"rejectInvalid\"\r\n }, \r\n \"layers\" : [\r\n {\r\n \"id\" : 0, \r\n \"name\" : \"Orange_County_Buildings\", \r\n \"parentLayerId\" : -1, \r\n \"defaultVisibility\" : true, \r\n \"subLayerIds\" : null, \r\n \"minScale\" : 160000, \r\n \"maxScale\" : 0, \r\n \"geometryType\" : \"esriGeometryPolygon\"\r\n }\r\n ], \r\n \"tables\" : []\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Thu, 28 May 2020 02:43:00 GMT", - "ETag" : "-1513483306", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|b0293fee4207a146986fc77b3404f762.8f95b5b4_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_1", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:37:03 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 4b2d2d4c49521bf403522140b78283a1.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "5DRDMxs0G_McyvwFrt8QSpFrudtSn7H4dtTB_lKGAuPW1qSSMyUmig==" - } - }, - "uuid" : "757edf0f-0dc4-4b42-869e-1bc2cf3ecf31", - "persistent" : true, - "insertionIndex" : 37 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_featureserver_0-e759c7d0-92ff-4db5-9bd8-25c500797126.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_featureserver_0-e759c7d0-92ff-4db5-9bd8-25c500797126.json deleted file mode 100644 index 056d9108..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_featureserver_0-e759c7d0-92ff-4db5-9bd8-25c500797126.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "e759c7d0-92ff-4db5-9bd8-25c500797126", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_featureserver_0", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Orange_County_CA_Buildings/FeatureServer/0", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"id\" : 0, \r\n \"name\" : \"Orange_County_Buildings\", \r\n \"type\" : \"Feature Layer\", \r\n \"serviceItemId\" : \"9ba701980ab94d8db87524ac6911f862\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"displayField\" : \"addr_housenumber\", \r\n \"description\" : \"This layer contains the pre-processed building polygons for the Orange County, California import to OpenStreetMap.\", \r\n \"copyrightText\" : \"Orange County\", \r\n \"defaultVisibility\" : true, \r\n \"editingInfo\" : {\r\n \"lastEditDate\" : 1590633780987\r\n }, \r\n \"relationships\" : [], \r\n \"isDataVersioned\" : false, \r\n \"supportsAppend\" : true, \r\n \"supportsCalculate\" : true, \r\n \"supportsASyncCalculate\" : true, \r\n \"supportsTruncate\" : false, \r\n \"supportsAttachmentsByUploadId\" : true, \r\n \"supportsAttachmentsResizing\" : true, \r\n \"supportsRollbackOnFailureParameter\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsExceedsLimitStatistics\" : true, \r\n \"supportsAdvancedQueries\" : true, \r\n \"supportsValidateSql\" : true, \r\n \"supportsCoordinatesQuantization\" : true, \r\n \"supportsFieldDescriptionProperty\" : true, \r\n \"supportsQuantizationEditMode\" : true, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsMultiScaleGeometry\" : true, \r\n \"supportsReturningQueryGeometry\" : true, \r\n \"hasGeometryProperties\" : true, \r\n \"geometryProperties\" : \r\n {\r\n \"shapeAreaFieldName\" : \"Shape__Area\", \r\n \"shapeLengthFieldName\" : \"Shape__Length\", \r\n \"units\" : \"esriDecimalDegrees\"\r\n }, \r\n \"advancedQueryCapabilities\" : {\r\n \"supportsPagination\" : true, \r\n \"supportsPaginationOnAggregatedQueries\" : true, \r\n \"supportsQueryRelatedPagination\" : true, \r\n \"supportsQueryWithDistance\" : true, \r\n \"supportsReturningQueryExtent\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsOrderBy\" : true, \r\n \"supportsDistinct\" : true, \r\n \"supportsQueryWithResultType\" : true, \r\n \"supportsSqlExpression\" : true, \r\n \"supportsAdvancedQueryRelated\" : true, \r\n \"supportsCountDistinct\" : true, \r\n \"supportsPercentileStatistics\" : true, \r\n \"supportsLod\" : true, \r\n \"supportsQueryWithLodSR\" : false, \r\n \"supportedLodTypes\" : [\r\n \"geohash\"\r\n ], \r\n \"supportsReturningGeometryCentroid\" : true, \r\n \"supportsReturningGeometryProperties\" : true, \r\n \"supportsQueryWithDatumTransformation\" : true, \r\n \"supportsHavingClause\" : true, \r\n \"supportsOutFieldSQLExpression\" : true, \r\n \"supportsMaxRecordCountFactor\" : true, \r\n \"supportsTopFeaturesQuery\" : true, \r\n \"supportsDisjointSpatialRel\" : true, \r\n \"supportsQueryWithCacheHint\" : true\r\n }, \r\n \"useStandardizedQueries\" : true, \r\n \"geometryType\" : \"esriGeometryPolygon\", \r\n \"minScale\" : 160000, \r\n \"maxScale\" : 0, \r\n \"extent\" : {\r\n \"xmin\" : -118.12928314353422, \r\n \"ymin\" : 33.381942817168351, \r\n \"xmax\" : -117.39614201190716, \r\n \"ymax\" : 33.9621048644816, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"drawingInfo\":{\"renderer\":{\"type\":\"uniqueValue\",\"field1\":\"Import_2_OSM\",\"uniqueValueInfos\":[{\"value\":\"1\",\"symbol\":{\"color\":[56,168,0,255],\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"},\"type\":\"esriSFS\",\"style\":\"esriSFSSolid\"},\"label\":\"Import\"},{\"value\":\"0\",\"symbol\":{\"color\":[168,0,0,255],\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"},\"type\":\"esriSFS\",\"style\":\"esriSFSSolid\"},\"label\":\"No Import\"}]},\"transparency\":20}, \r\n \"allowGeometryUpdates\" : true, \r\n \"hasAttachments\" : false, \r\n \"viewSourceHasAttachments\" : false, \r\n \"htmlPopupType\" : \"esriServerHTMLPopupTypeAsHTMLText\", \r\n \"hasMetadata\" : true, \r\n \"hasM\" : false, \r\n \"hasZ\" : false, \r\n \"objectIdField\" : \"OBJECTID\", \r\n \"uniqueIdField\" : \r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"isSystemMaintained\" : true\r\n }, \r\n \"globalIdField\" : \"\", \r\n \"typeIdField\" : \"Import_2_OSM\", \r\n \"fields\" : [\r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"type\" : \"esriFieldTypeOID\", \r\n \"alias\" : \"OBJECTID\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : false, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BLDG_RECNO\", \r\n \"type\" : \"esriFieldTypeInteger\", \r\n \"alias\" : \"BLDG_RECNO\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_housenumber\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:housenumber\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_street\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:street\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_unit\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:unit\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 20, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_city\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:city\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_state\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:state\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 2, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_postcode\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:postcode\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"height\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"height\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Import_2_OSM\", \r\n \"type\" : \"esriFieldTypeSmallInteger\", \r\n \"alias\" : \"Import_2_OSM\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BlockGroup\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"BlockGroup\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 12, \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Shape__Area\", \r\n \"type\" : \"esriFieldTypeDouble\", \r\n \"alias\" : \"Shape__Area\", \r\n \"sqlType\" : \"sqlTypeDouble\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Shape__Length\", \r\n \"type\" : \"esriFieldTypeDouble\", \r\n \"alias\" : \"Shape__Length\", \r\n \"sqlType\" : \"sqlTypeDouble\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }\r\n ], \r\n \"indexes\" : [\r\n {\r\n \"name\" : \"PK__ORANGECO__F4B70D85FA4F55F5\", \r\n \"fields\" : \"OBJECTID\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : true, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"user_58277.ORANGECOUNTY_BUILDINGS_ORANGECOUNTY_BUILDINGS_Shape_sidx\", \r\n \"fields\" : \"Shape\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"Shape__Area_Index\", \r\n \"fields\" : \"Shape__Area\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"Shape__Length_Index\", \r\n \"fields\" : \"Shape__Length\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }\r\n ], \r\n \"types\" : [\r\n {\r\n \"id\" : 1, \r\n \"name\" : \"1\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"1\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 1, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null, \r\n \"height\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }, \r\n {\r\n \"id\" : 0, \r\n \"name\" : \"0\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"0\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 0, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null, \r\n \"height\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }\r\n ], \r\n \"templates\" : [], \r\n \"supportedQueryFormats\" : \"JSON, geoJSON, PBF\", \r\n \"hasStaticData\" : false, \r\n \"maxRecordCount\" : 2000, \r\n \"standardMaxRecordCount\" : 4000, \r\n \"standardMaxRecordCountNoGeometry\" : 32000, \r\n \"tileMaxRecordCount\" : 4000, \r\n \"maxRecordCountFactor\" : 1, \r\n \"capabilities\" : \"Query\"\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Thu, 28 May 2020 02:43:00 GMT", - "ETag" : "-707854555", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|26335c467a6ad845a391a4c4f5fa47e5.20755a7e_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_28", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:51:07 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 045f1e1f031241f3808c557a2b5d6b0a.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "PWxSTFielY46Tp7YxdhI7IEkFGn8wQv-aGw23dCeswTfkHsX3G5EHw==" - } - }, - "uuid" : "e759c7d0-92ff-4db5-9bd8-25c500797126", - "persistent" : true, - "insertionIndex" : 33 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_v2_featureserver-e0115f5a-2f2f-4c3c-bd23-97d8b183e7f7.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_v2_featureserver-e0115f5a-2f2f-4c3c-bd23-97d8b183e7f7.json deleted file mode 100644 index 01d2fa7c..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_v2_featureserver-e0115f5a-2f2f-4c3c-bd23-97d8b183e7f7.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "id" : "e0115f5a-2f2f-4c3c-bd23-97d8b183e7f7", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_orange_county_ca_buildings_v2_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Orange_County_CA_Buildings_v2/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:22:43 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "e0115f5a-2f2f-4c3c-bd23-97d8b183e7f7", - "persistent" : true, - "scenarioName" : "scenario-7-Do88DoK2xjTUCXd1-arcgis-rest-services-Orange_County_CA_Buildings_v2-FeatureServer", - "requiredScenarioState" : "Started", - "insertionIndex" : 67 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_pottawattamie_county_ia_buildings_featureserver-146e68bf-e36f-4d1a-9670-5cdbe2c3f636.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_pottawattamie_county_ia_buildings_featureserver-146e68bf-e36f-4d1a-9670-5cdbe2c3f636.json deleted file mode 100644 index ce496e74..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_pottawattamie_county_ia_buildings_featureserver-146e68bf-e36f-4d1a-9670-5cdbe2c3f636.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "146e68bf-e36f-4d1a-9670-5cdbe2c3f636", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_pottawattamie_county_ia_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Pottawattamie_County_IA_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:19:56 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "146e68bf-e36f-4d1a-9670-5cdbe2c3f636", - "persistent" : true, - "insertionIndex" : 55 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_richland_county_nd_addresses_featureserver-6b8d2ee4-b32f-4e81-80d1-bdf6bf8bba3f.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_richland_county_nd_addresses_featureserver-6b8d2ee4-b32f-4e81-80d1-bdf6bf8bba3f.json deleted file mode 100644 index aac8dae3..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_richland_county_nd_addresses_featureserver-6b8d2ee4-b32f-4e81-80d1-bdf6bf8bba3f.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "6b8d2ee4-b32f-4e81-80d1-bdf6bf8bba3f", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_richland_county_nd_addresses_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Richland_County_ND_Addresses/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:06:14 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "6b8d2ee4-b32f-4e81-80d1-bdf6bf8bba3f", - "persistent" : true, - "insertionIndex" : 50 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_featureserver-1b8f292c-d3b0-4281-ad07-680b02579e6b.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_featureserver-1b8f292c-d3b0-4281-ad07-680b02579e6b.json deleted file mode 100644 index 43d5fb21..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_featureserver-1b8f292c-d3b0-4281-ad07-680b02579e6b.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "1b8f292c-d3b0-4281-ad07-680b02579e6b", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Riverside_CA_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"serviceItemId\" : \"37ff44d836fd4a2d9a80b609e3d589c6\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"serviceDescription\" : \"\", \r\n \"hasVersionedData\" : false, \r\n \"supportsDisconnectedEditing\" : false, \r\n \"hasStaticData\" : false, \r\n \"hasSharedDomains\" : false, \r\n \"maxRecordCount\" : 1000, \r\n \"supportedQueryFormats\" : \"JSON\", \r\n \"supportsVCSProjection\" : false, \r\n \"capabilities\" : \"Query\", \r\n \"description\" : \"\", \r\n \"copyrightText\" : \"\", \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }, \r\n \"initialExtent\" : {\r\n \"xmin\" : -117.5146144105976, \r\n \"ymin\" : 33.844485330883067, \r\n \"xmax\" : -117.26599679190556, \r\n \"ymax\" : 34.034093431983059, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"fullExtent\" : {\r\n \"xmin\" : -117.54107499999998, \r\n \"ymin\" : 33.858206000000052, \r\n \"xmax\" : -117.26151999999996, \r\n \"ymax\" : 34.03057700100004, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"allowGeometryUpdates\" : true, \r\n \"units\" : \"esriDecimalDegrees\", \r\n \"supportsAppend\" : true, \r\n \"supportsSharedDomains\" : true, \r\n \"syncEnabled\" : false, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturnDeleteResults\" : true, \r\n \"xssPreventionInfo\" : {\r\n \"xssPreventionEnabled\" : true, \r\n \"xssPreventionRule\" : \"InputOnly\", \r\n \"xssInputRule\" : \"rejectInvalid\"\r\n }, \r\n \"layers\" : [\r\n {\r\n \"id\" : 0, \r\n \"name\" : \"Riverside_buildings\", \r\n \"parentLayerId\" : -1, \r\n \"defaultVisibility\" : true, \r\n \"subLayerIds\" : null, \r\n \"minScale\" : 40000, \r\n \"maxScale\" : 0, \r\n \"geometryType\" : \"esriGeometryPolygon\"\r\n }\r\n ], \r\n \"tables\" : []\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Fri, 24 Apr 2020 15:26:47 GMT", - "ETag" : "-917103859", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|51bfce8c109b994bb7a28544d39352e8.a8a777e_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_19", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:36:23 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 4b2d2d4c49521bf403522140b78283a1.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "KMfmDJNR0r65x0Xg5sYCeExYPqXoAMNd5daa0nOLWjuLLe2cP7NSjw==" - } - }, - "uuid" : "1b8f292c-d3b0-4281-ad07-680b02579e6b", - "persistent" : true, - "insertionIndex" : 25 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_featureserver_0-9bba8228-60ea-4b25-b140-ea7b4aeb79ab.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_featureserver_0-9bba8228-60ea-4b25-b140-ea7b4aeb79ab.json deleted file mode 100644 index 50e52c34..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_featureserver_0-9bba8228-60ea-4b25-b140-ea7b4aeb79ab.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "id" : "9bba8228-60ea-4b25-b140-ea7b4aeb79ab", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_featureserver_0", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Riverside_CA_Buildings/FeatureServer/0", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"id\" : 0, \r\n \"name\" : \"Riverside_buildings\", \r\n \"type\" : \"Feature Layer\", \r\n \"serviceItemId\" : \"37ff44d836fd4a2d9a80b609e3d589c6\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"displayField\" : \"name\", \r\n \"description\" : \"This layer contains the pre-processed building polygons for the City of Riverside import to OpenStreetMap.\", \r\n \"copyrightText\" : \"City of Riverside\", \r\n \"defaultVisibility\" : true, \r\n \"editingInfo\" : {\r\n \"lastEditDate\" : 1587742007422\r\n }, \r\n \"relationships\" : [], \r\n \"isDataVersioned\" : false, \r\n \"supportsAppend\" : true, \r\n \"supportsCalculate\" : true, \r\n \"supportsASyncCalculate\" : true, \r\n \"supportsTruncate\" : false, \r\n \"supportsAttachmentsByUploadId\" : true, \r\n \"supportsAttachmentsResizing\" : true, \r\n \"supportsRollbackOnFailureParameter\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsExceedsLimitStatistics\" : true, \r\n \"supportsAdvancedQueries\" : true, \r\n \"supportsValidateSql\" : true, \r\n \"supportsCoordinatesQuantization\" : true, \r\n \"supportsFieldDescriptionProperty\" : true, \r\n \"supportsQuantizationEditMode\" : true, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsMultiScaleGeometry\" : true, \r\n \"supportsReturningQueryGeometry\" : true, \r\n \"hasGeometryProperties\" : true, \r\n \"geometryProperties\" : \r\n {\r\n \"shapeAreaFieldName\" : \"Shape__Area\", \r\n \"shapeLengthFieldName\" : \"Shape__Length\", \r\n \"units\" : \"esriDecimalDegrees\"\r\n }, \r\n \"advancedQueryCapabilities\" : {\r\n \"supportsPagination\" : true, \r\n \"supportsPaginationOnAggregatedQueries\" : true, \r\n \"supportsQueryRelatedPagination\" : true, \r\n \"supportsQueryWithDistance\" : true, \r\n \"supportsReturningQueryExtent\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsOrderBy\" : true, \r\n \"supportsDistinct\" : true, \r\n \"supportsQueryWithResultType\" : true, \r\n \"supportsSqlExpression\" : true, \r\n \"supportsAdvancedQueryRelated\" : true, \r\n \"supportsCountDistinct\" : true, \r\n \"supportsPercentileStatistics\" : true, \r\n \"supportsLod\" : true, \r\n \"supportsQueryWithLodSR\" : false, \r\n \"supportedLodTypes\" : [\r\n \"geohash\"\r\n ], \r\n \"supportsReturningGeometryCentroid\" : true, \r\n \"supportsReturningGeometryProperties\" : true, \r\n \"supportsQueryWithDatumTransformation\" : true, \r\n \"supportsHavingClause\" : true, \r\n \"supportsOutFieldSQLExpression\" : true, \r\n \"supportsMaxRecordCountFactor\" : true, \r\n \"supportsTopFeaturesQuery\" : true, \r\n \"supportsDisjointSpatialRel\" : true, \r\n \"supportsQueryWithCacheHint\" : true\r\n }, \r\n \"useStandardizedQueries\" : true, \r\n \"geometryType\" : \"esriGeometryPolygon\", \r\n \"minScale\" : 40000, \r\n \"maxScale\" : 0, \r\n \"extent\" : {\r\n \"xmin\" : -117.54107499999998, \r\n \"ymin\" : 33.858206000000052, \r\n \"xmax\" : -117.26151999999996, \r\n \"ymax\" : 34.03057700100004, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"drawingInfo\":{\"renderer\":{\"type\":\"uniqueValue\",\"field1\":\"Import_2_OSM\",\"uniqueValueInfos\":[{\"value\":\"1\",\"symbol\":{\"color\":[56,168,0,255],\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"},\"type\":\"esriSFS\",\"style\":\"esriSFSSolid\"},\"label\":\"Import\"},{\"value\":\"0\",\"symbol\":{\"color\":[168,0,0,255],\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"},\"type\":\"esriSFS\",\"style\":\"esriSFSSolid\"},\"label\":\"No Import\"}]},\"transparency\":20}, \r\n \"allowGeometryUpdates\" : true, \r\n \"hasAttachments\" : false, \r\n \"viewSourceHasAttachments\" : false, \r\n \"htmlPopupType\" : \"esriServerHTMLPopupTypeAsHTMLText\", \r\n \"hasMetadata\" : true, \r\n \"hasM\" : false, \r\n \"hasZ\" : false, \r\n \"objectIdField\" : \"OBJECTID\", \r\n \"uniqueIdField\" : \r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"isSystemMaintained\" : true\r\n }, \r\n \"globalIdField\" : \"\", \r\n \"typeIdField\" : \"Import_2_OSM\", \r\n \"fields\" : [\r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"type\" : \"esriFieldTypeOID\", \r\n \"alias\" : \"OBJECTID\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : false, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"building\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"building\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_housenumber\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:housenumber\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_street\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:street\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_city\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:city\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 20, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_state\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:state\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 2, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"name\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"name\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 100, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"height\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"height\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"levels\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"levels\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Import_2_OSM\", \r\n \"type\" : \"esriFieldTypeSmallInteger\", \r\n \"alias\" : \"Import_2_OSM\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BlockGroup\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"Block Group\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 20, \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }\r\n ], \r\n \"indexes\" : [\r\n {\r\n \"name\" : \"PK__RIVERSID__F4B70D856F272E3D\", \r\n \"fields\" : \"OBJECTID\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : true, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"user_58277.RIVERSIDE_BUILDINGS_RIVERSIDE_BUILDINGS2_Shape_sidx\", \r\n \"fields\" : \"Shape\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }\r\n ], \r\n \"types\" : [\r\n {\r\n \"id\" : 1, \r\n \"name\" : \"1\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"1\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 1, \r\n \"building\" : null, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"name\" : null, \r\n \"height\" : null, \r\n \"levels\" : null, \r\n \"BlockGroup\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }, \r\n {\r\n \"id\" : 0, \r\n \"name\" : \"0\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"0\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 0, \r\n \"building\" : null, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"name\" : null, \r\n \"height\" : null, \r\n \"levels\" : null, \r\n \"BlockGroup\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }\r\n ], \r\n \"templates\" : [], \r\n \"supportedQueryFormats\" : \"JSON, geoJSON, PBF\", \r\n \"hasStaticData\" : false, \r\n \"maxRecordCount\" : 2000, \r\n \"standardMaxRecordCount\" : 4000, \r\n \"standardMaxRecordCountNoGeometry\" : 32000, \r\n \"tileMaxRecordCount\" : 4000, \r\n \"maxRecordCountFactor\" : 1, \r\n \"capabilities\" : \"Query\"\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Fri, 24 Apr 2020 15:26:47 GMT", - "ETag" : "-823573638", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|27832cbbd6601846a99c6f33aca16035.8466cdfb_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_20", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:53:27 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 23241b9c368643949e3bb1a1ba4e97c3.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "XMDdr-YBC4C9_49w4gA7c82m2jwArGT9dTe3ka-qFjZ2wJh-I9nahA==" - } - }, - "uuid" : "9bba8228-60ea-4b25-b140-ea7b4aeb79ab", - "persistent" : true, - "scenarioName" : "scenario-2-Do88DoK2xjTUCXd1-arcgis-rest-services-Riverside_CA_Buildings-FeatureServer-0", - "requiredScenarioState" : "Started", - "insertionIndex" : 39 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_v2_featureserver-646c0289-b022-4f04-a4e2-b402a256f18d.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_v2_featureserver-646c0289-b022-4f04-a4e2-b402a256f18d.json deleted file mode 100644 index a37a2e44..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_v2_featureserver-646c0289-b022-4f04-a4e2-b402a256f18d.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "id" : "646c0289-b022-4f04-a4e2-b402a256f18d", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_riverside_ca_buildings_v2_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Riverside_CA_Buildings_v2/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:22:34 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "646c0289-b022-4f04-a4e2-b402a256f18d", - "persistent" : true, - "scenarioName" : "scenario-6-Do88DoK2xjTUCXd1-arcgis-rest-services-Riverside_CA_Buildings_v2-FeatureServer", - "requiredScenarioState" : "Started", - "insertionIndex" : 66 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sandiego_county_ca_addresses_featureserver-35444ef3-ef5b-4ba3-b2e6-8eaa2b18bb4c.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sandiego_county_ca_addresses_featureserver-35444ef3-ef5b-4ba3-b2e6-8eaa2b18bb4c.json deleted file mode 100644 index c01ee88a..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sandiego_county_ca_addresses_featureserver-35444ef3-ef5b-4ba3-b2e6-8eaa2b18bb4c.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "35444ef3-ef5b-4ba3-b2e6-8eaa2b18bb4c", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_sandiego_county_ca_addresses_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/SanDiego_County_CA_Addresses/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:05:38 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "35444ef3-ef5b-4ba3-b2e6-8eaa2b18bb4c", - "persistent" : true, - "insertionIndex" : 48 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sangis_buildings_featureserver-7100a55a-11f9-4a63-ab7e-27e463171e03.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sangis_buildings_featureserver-7100a55a-11f9-4a63-ab7e-27e463171e03.json deleted file mode 100644 index 24951d9d..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sangis_buildings_featureserver-7100a55a-11f9-4a63-ab7e-27e463171e03.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "id" : "7100a55a-11f9-4a63-ab7e-27e463171e03", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_sangis_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/SanGIS_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:22:27 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "7100a55a-11f9-4a63-ab7e-27e463171e03", - "persistent" : true, - "scenarioName" : "scenario-5-Do88DoK2xjTUCXd1-arcgis-rest-services-SanGIS_Buildings-FeatureServer", - "requiredScenarioState" : "Started", - "insertionIndex" : 65 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_addresses_featureserver-7950efe2-fcb1-4457-9b3a-fd281eb49684.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_addresses_featureserver-7950efe2-fcb1-4457-9b3a-fd281eb49684.json deleted file mode 100644 index 34fa188e..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_addresses_featureserver-7950efe2-fcb1-4457-9b3a-fd281eb49684.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "7950efe2-fcb1-4457-9b3a-fd281eb49684", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_addresses_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Sarpy_County_NE_Addresses/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"serviceItemId\" : \"9a0a7e1a462b44f0ad043e92fc72ddd3\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"preserveLayerIds\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"serviceDescription\" : \"\", \r\n \"hasVersionedData\" : false, \r\n \"supportsDisconnectedEditing\" : false, \r\n \"hasStaticData\" : false, \r\n \"hasSharedDomains\" : false, \r\n \"maxRecordCount\" : 1000, \r\n \"supportedQueryFormats\" : \"JSON\", \r\n \"supportsVCSProjection\" : false, \r\n \"capabilities\" : \"Query\", \r\n \"description\" : \"\", \r\n \"copyrightText\" : \"\", \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }, \r\n \"initialExtent\" : {\r\n \"xmin\" : -96.35594832668562, \r\n \"ymin\" : 40.897946398790609, \r\n \"xmax\" : -95.84048423516316, \r\n \"ymax\" : 41.29106482737582, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"fullExtent\" : {\r\n \"xmin\" : -96.330894031134449, \r\n \"ymin\" : 40.99807955401792, \r\n \"xmax\" : -95.865538530714332, \r\n \"ymax\" : 41.190931672148515, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"allowGeometryUpdates\" : true, \r\n \"units\" : \"esriDecimalDegrees\", \r\n \"supportsAppend\" : true, \r\n \"supportsSharedDomains\" : true, \r\n \"syncEnabled\" : false, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturnDeleteResults\" : true, \r\n \"xssPreventionInfo\" : {\r\n \"xssPreventionEnabled\" : true, \r\n \"xssPreventionRule\" : \"InputOnly\", \r\n \"xssInputRule\" : \"rejectInvalid\"\r\n }, \r\n \"layers\" : [\r\n {\r\n \"id\" : 1, \r\n \"name\" : \"Sarpy_County_Addresses\", \r\n \"parentLayerId\" : -1, \r\n \"defaultVisibility\" : true, \r\n \"subLayerIds\" : null, \r\n \"minScale\" : 0, \r\n \"maxScale\" : 0, \r\n \"geometryType\" : \"esriGeometryPoint\"\r\n }\r\n ], \r\n \"tables\" : []\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Sun, 17 May 2020 21:01:28 GMT", - "ETag" : "869591283", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|41c34b04a88bce40b3e74799c02ccf53.a8a769f_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_19", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:36:39 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 4b2d2d4c49521bf403522140b78283a1.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "s4jvzvt6SVXiaO67P8FrkN5rl85bM04VYjEkhZ4h3-Rfx4kq_BfVzw==" - } - }, - "uuid" : "7950efe2-fcb1-4457-9b3a-fd281eb49684", - "persistent" : true, - "insertionIndex" : 29 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_addresses_featureserver_1-89bb3fed-c485-401c-a9b9-9c5b367f81a0.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_addresses_featureserver_1-89bb3fed-c485-401c-a9b9-9c5b367f81a0.json deleted file mode 100644 index d8528ce9..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_addresses_featureserver_1-89bb3fed-c485-401c-a9b9-9c5b367f81a0.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "89bb3fed-c485-401c-a9b9-9c5b367f81a0", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_addresses_featureserver_1", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Sarpy_County_NE_Addresses/FeatureServer/1", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"id\" : 1, \r\n \"name\" : \"Sarpy_County_Addresses\", \r\n \"type\" : \"Feature Layer\", \r\n \"serviceItemId\" : \"9a0a7e1a462b44f0ad043e92fc72ddd3\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"displayField\" : \"name\", \r\n \"description\" : \"This layer package contains the pre-processed address points for Sarpy County, Nebraska import to OpenStreetMap.\", \r\n \"copyrightText\" : \"Sarpy County\", \r\n \"defaultVisibility\" : true, \r\n \"editingInfo\" : {\r\n \"lastEditDate\" : 1589749288526\r\n }, \r\n \"relationships\" : [], \r\n \"isDataVersioned\" : false, \r\n \"supportsAppend\" : true, \r\n \"supportsCalculate\" : true, \r\n \"supportsASyncCalculate\" : true, \r\n \"supportsTruncate\" : false, \r\n \"supportsAttachmentsByUploadId\" : true, \r\n \"supportsAttachmentsResizing\" : true, \r\n \"supportsRollbackOnFailureParameter\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsExceedsLimitStatistics\" : true, \r\n \"supportsAdvancedQueries\" : true, \r\n \"supportsValidateSql\" : true, \r\n \"supportsCoordinatesQuantization\" : true, \r\n \"supportsFieldDescriptionProperty\" : true, \r\n \"supportsQuantizationEditMode\" : true, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturningQueryGeometry\" : true, \r\n \"advancedQueryCapabilities\" : {\r\n \"supportsPagination\" : true, \r\n \"supportsPaginationOnAggregatedQueries\" : true, \r\n \"supportsQueryRelatedPagination\" : true, \r\n \"supportsQueryWithDistance\" : true, \r\n \"supportsReturningQueryExtent\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsOrderBy\" : true, \r\n \"supportsDistinct\" : true, \r\n \"supportsQueryWithResultType\" : true, \r\n \"supportsSqlExpression\" : true, \r\n \"supportsAdvancedQueryRelated\" : true, \r\n \"supportsCountDistinct\" : true, \r\n \"supportsPercentileStatistics\" : true, \r\n \"supportsLod\" : true, \r\n \"supportsQueryWithLodSR\" : false, \r\n \"supportedLodTypes\" : [\r\n \"geohash\"\r\n ], \r\n \"supportsReturningGeometryCentroid\" : false, \r\n \"supportsQueryWithDatumTransformation\" : true, \r\n \"supportsHavingClause\" : true, \r\n \"supportsOutFieldSQLExpression\" : true, \r\n \"supportsMaxRecordCountFactor\" : true, \r\n \"supportsTopFeaturesQuery\" : true, \r\n \"supportsDisjointSpatialRel\" : true, \r\n \"supportsQueryWithCacheHint\" : true\r\n }, \r\n \"useStandardizedQueries\" : true, \r\n \"geometryType\" : \"esriGeometryPoint\", \r\n \"minScale\" : 0, \r\n \"maxScale\" : 0, \r\n \"extent\" : {\r\n \"xmin\" : -96.330894031134449, \r\n \"ymin\" : 40.99807955401792, \r\n \"xmax\" : -95.865538530714332, \r\n \"ymax\" : 41.190931672148515, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"drawingInfo\":{\"renderer\":{\"type\":\"uniqueValue\",\"field1\":\"Import_2_OSM\",\"uniqueValueInfos\":[{\"value\":\"0\",\"symbol\":{\"color\":[0,77,168,255],\"size\":6,\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriSMS\",\"style\":\"esriSMSCircle\",\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"}},\"label\":\"No Import (Building)\"},{\"value\":\"1\",\"symbol\":{\"color\":[56,168,0,255],\"size\":6,\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriSMS\",\"style\":\"esriSMSCircle\",\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"}},\"label\":\"Import\"},{\"value\":\"2\",\"symbol\":{\"color\":[168,0,0,255],\"size\":6,\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriSMS\",\"style\":\"esriSMSCircle\",\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"}},\"label\":\"No Import (Conflict)\"}]},\"transparency\":0}, \r\n \"allowGeometryUpdates\" : true, \r\n \"hasAttachments\" : false, \r\n \"viewSourceHasAttachments\" : false, \r\n \"htmlPopupType\" : \"esriServerHTMLPopupTypeAsHTMLText\", \r\n \"hasMetadata\" : true, \r\n \"hasM\" : false, \r\n \"hasZ\" : false, \r\n \"objectIdField\" : \"OBJECTID\", \r\n \"uniqueIdField\" : \r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"isSystemMaintained\" : true\r\n }, \r\n \"globalIdField\" : \"\", \r\n \"typeIdField\" : \"Import_2_OSM\", \r\n \"fields\" : [\r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"type\" : \"esriFieldTypeOID\", \r\n \"alias\" : \"OBJECTID\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : false, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"ADDR_RECNO\", \r\n \"type\" : \"esriFieldTypeInteger\", \r\n \"alias\" : \"ADDR_RECNO\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_housenumber\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:housenumber\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_street\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:street\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_unit\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:unit\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 20, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_city\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:city\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 20, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_state\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:state\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 20, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_postcode\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:postcode\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 5, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"name\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"name\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 100, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BLDG_RECNO\", \r\n \"type\" : \"esriFieldTypeInteger\", \r\n \"alias\" : \"BLDG_RECNO\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BlockGroup\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"BlockGroup\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 12, \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Import_2_OSM\", \r\n \"type\" : \"esriFieldTypeSmallInteger\", \r\n \"alias\" : \"Import_2_OSM\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }\r\n ], \r\n \"indexes\" : [\r\n {\r\n \"name\" : \"PK__SARPY_CO__F4B70D85BC15EE4C\", \r\n \"fields\" : \"OBJECTID\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : true, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"user_58277.SARPY_COUNTY_ADDRESSES_SARPY_COUNTY_ADDRESSES_Shape_sidx\", \r\n \"fields\" : \"Shape\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }\r\n ], \r\n \"types\" : [\r\n {\r\n \"id\" : 0, \r\n \"name\" : \"0\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"0\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 0, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null, \r\n \"name\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }, \r\n {\r\n \"id\" : 1, \r\n \"name\" : \"1\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"1\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 1, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null, \r\n \"name\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }, \r\n {\r\n \"id\" : 2, \r\n \"name\" : \"2\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"2\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 2, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null, \r\n \"name\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }\r\n ], \r\n \"templates\" : [], \r\n \"supportedQueryFormats\" : \"JSON, geoJSON, PBF\", \r\n \"hasStaticData\" : false, \r\n \"maxRecordCount\" : 2000, \r\n \"standardMaxRecordCount\" : 32000, \r\n \"standardMaxRecordCountNoGeometry\" : 32000, \r\n \"tileMaxRecordCount\" : 8000, \r\n \"maxRecordCountFactor\" : 1, \r\n \"capabilities\" : \"Query\"\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Sun, 17 May 2020 21:01:28 GMT", - "ETag" : "-1597234668", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|9419871cc231974191f7ddaa4f316530.8f95b760_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_1", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:51:51 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 045f1e1f031241f3808c557a2b5d6b0a.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "kMOca3eo_60H_TVy6qbX8EWUNPt0xKbHMK0aTSpnpESVSGDGKkBI1w==" - } - }, - "uuid" : "89bb3fed-c485-401c-a9b9-9c5b367f81a0", - "persistent" : true, - "insertionIndex" : 37 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_buildings_featureserver-1ab90e03-6781-4f0b-97c0-9e66b728c81c.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_buildings_featureserver-1ab90e03-6781-4f0b-97c0-9e66b728c81c.json deleted file mode 100644 index ab5dd260..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_buildings_featureserver-1ab90e03-6781-4f0b-97c0-9e66b728c81c.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "1ab90e03-6781-4f0b-97c0-9e66b728c81c", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Sarpy_County_NE_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"serviceItemId\" : \"6c6eadce1c6444aa97bebe20bf0fe507\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"preserveLayerIds\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"serviceDescription\" : \"\", \r\n \"hasVersionedData\" : false, \r\n \"supportsDisconnectedEditing\" : false, \r\n \"hasStaticData\" : false, \r\n \"hasSharedDomains\" : false, \r\n \"maxRecordCount\" : 1000, \r\n \"supportedQueryFormats\" : \"JSON\", \r\n \"supportsVCSProjection\" : false, \r\n \"capabilities\" : \"Query\", \r\n \"description\" : \"\", \r\n \"copyrightText\" : \"\", \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }, \r\n \"initialExtent\" : {\r\n \"xmin\" : -96.357334293042641, \r\n \"ymin\" : 40.8979083176391, \r\n \"xmax\" : -95.841354076957316, \r\n \"ymax\" : 41.291420368361024, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"fullExtent\" : {\r\n \"xmin\" : -96.333788256999981, \r\n \"ymin\" : 40.998342824000083, \r\n \"xmax\" : -95.864900112999962, \r\n \"ymax\" : 41.190985862000048, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"allowGeometryUpdates\" : true, \r\n \"units\" : \"esriDecimalDegrees\", \r\n \"supportsAppend\" : true, \r\n \"supportsSharedDomains\" : true, \r\n \"syncEnabled\" : false, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturnDeleteResults\" : true, \r\n \"xssPreventionInfo\" : {\r\n \"xssPreventionEnabled\" : true, \r\n \"xssPreventionRule\" : \"InputOnly\", \r\n \"xssInputRule\" : \"rejectInvalid\"\r\n }, \r\n \"layers\" : [\r\n {\r\n \"id\" : 1, \r\n \"name\" : \"Sarpy_County_Buildings\", \r\n \"parentLayerId\" : -1, \r\n \"defaultVisibility\" : true, \r\n \"subLayerIds\" : null, \r\n \"minScale\" : 0, \r\n \"maxScale\" : 0, \r\n \"geometryType\" : \"esriGeometryPolygon\"\r\n }\r\n ], \r\n \"tables\" : []\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Sun, 17 May 2020 20:17:05 GMT", - "ETag" : "-189569333", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|17ee85e28c7dfc43bd4ce4d7e7f336c4.4c15f059_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_21", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:36:32 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 4b2d2d4c49521bf403522140b78283a1.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "lGqTOwx17CXM2CIt9OhznvtFs_mHVZecOgJnxGBkX6BDBF73n-3V5A==" - } - }, - "uuid" : "1ab90e03-6781-4f0b-97c0-9e66b728c81c", - "persistent" : true, - "insertionIndex" : 27 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_buildings_featureserver_1-1edea050-3368-40f5-80ae-099dba138e0d.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_buildings_featureserver_1-1edea050-3368-40f5-80ae-099dba138e0d.json deleted file mode 100644 index 5217cd8c..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_buildings_featureserver_1-1edea050-3368-40f5-80ae-099dba138e0d.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "id" : "1edea050-3368-40f5-80ae-099dba138e0d", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_sarpy_county_ne_buildings_featureserver_1", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Sarpy_County_NE_Buildings/FeatureServer/1", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"id\" : 1, \r\n \"name\" : \"Sarpy_County_Buildings\", \r\n \"type\" : \"Feature Layer\", \r\n \"serviceItemId\" : \"6c6eadce1c6444aa97bebe20bf0fe507\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"displayField\" : \"name\", \r\n \"description\" : \"This layer contains the pre-processed building polygons for the Sarpy County, Nebraska import to OpenStreetMap\", \r\n \"copyrightText\" : \"Sarpy County\", \r\n \"defaultVisibility\" : true, \r\n \"editingInfo\" : {\r\n \"lastEditDate\" : 1589746625713\r\n }, \r\n \"relationships\" : [], \r\n \"isDataVersioned\" : false, \r\n \"supportsAppend\" : true, \r\n \"supportsCalculate\" : true, \r\n \"supportsASyncCalculate\" : true, \r\n \"supportsTruncate\" : false, \r\n \"supportsAttachmentsByUploadId\" : true, \r\n \"supportsAttachmentsResizing\" : true, \r\n \"supportsRollbackOnFailureParameter\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsExceedsLimitStatistics\" : true, \r\n \"supportsAdvancedQueries\" : true, \r\n \"supportsValidateSql\" : true, \r\n \"supportsCoordinatesQuantization\" : true, \r\n \"supportsFieldDescriptionProperty\" : true, \r\n \"supportsQuantizationEditMode\" : true, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsMultiScaleGeometry\" : true, \r\n \"supportsReturningQueryGeometry\" : true, \r\n \"hasGeometryProperties\" : true, \r\n \"geometryProperties\" : \r\n {\r\n \"shapeAreaFieldName\" : \"Shape__Area\", \r\n \"shapeLengthFieldName\" : \"Shape__Length\", \r\n \"units\" : \"esriDecimalDegrees\"\r\n }, \r\n \"advancedQueryCapabilities\" : {\r\n \"supportsPagination\" : true, \r\n \"supportsPaginationOnAggregatedQueries\" : true, \r\n \"supportsQueryRelatedPagination\" : true, \r\n \"supportsQueryWithDistance\" : true, \r\n \"supportsReturningQueryExtent\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsOrderBy\" : true, \r\n \"supportsDistinct\" : true, \r\n \"supportsQueryWithResultType\" : true, \r\n \"supportsSqlExpression\" : true, \r\n \"supportsAdvancedQueryRelated\" : true, \r\n \"supportsCountDistinct\" : true, \r\n \"supportsPercentileStatistics\" : true, \r\n \"supportsLod\" : true, \r\n \"supportsQueryWithLodSR\" : false, \r\n \"supportedLodTypes\" : [\r\n \"geohash\"\r\n ], \r\n \"supportsReturningGeometryCentroid\" : true, \r\n \"supportsReturningGeometryProperties\" : true, \r\n \"supportsQueryWithDatumTransformation\" : true, \r\n \"supportsHavingClause\" : true, \r\n \"supportsOutFieldSQLExpression\" : true, \r\n \"supportsMaxRecordCountFactor\" : true, \r\n \"supportsTopFeaturesQuery\" : true, \r\n \"supportsDisjointSpatialRel\" : true, \r\n \"supportsQueryWithCacheHint\" : true\r\n }, \r\n \"useStandardizedQueries\" : true, \r\n \"geometryType\" : \"esriGeometryPolygon\", \r\n \"minScale\" : 0, \r\n \"maxScale\" : 0, \r\n \"extent\" : {\r\n \"xmin\" : -96.333788256999981, \r\n \"ymin\" : 40.998342824000083, \r\n \"xmax\" : -95.864900112999962, \r\n \"ymax\" : 41.190985862000048, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"drawingInfo\":{\"renderer\":{\"type\":\"uniqueValue\",\"field1\":\"Import_2_OSM\",\"uniqueValueInfos\":[{\"value\":\"1\",\"symbol\":{\"color\":[56,168,0,255],\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"},\"type\":\"esriSFS\",\"style\":\"esriSFSSolid\"},\"label\":\"Import\"},{\"value\":\"0\",\"symbol\":{\"color\":[168,0,0,255],\"outline\":{\"color\":[153,153,153,64],\"width\":0.75,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"},\"type\":\"esriSFS\",\"style\":\"esriSFSSolid\"},\"label\":\"No Import\"}]},\"transparency\":20}, \r\n \"allowGeometryUpdates\" : true, \r\n \"hasAttachments\" : false, \r\n \"viewSourceHasAttachments\" : false, \r\n \"htmlPopupType\" : \"esriServerHTMLPopupTypeAsHTMLText\", \r\n \"hasMetadata\" : true, \r\n \"hasM\" : true, \r\n \"allowUpdateWithoutMValues\" : true, \r\n \"hasZ\" : true, \r\n \"enableZDefaults\" : true, \r\n \"zDefault\" : 0, \r\n \"objectIdField\" : \"OBJECTID\", \r\n \"uniqueIdField\" : \r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"isSystemMaintained\" : true\r\n }, \r\n \"globalIdField\" : \"\", \r\n \"typeIdField\" : \"Import_2_OSM\", \r\n \"fields\" : [\r\n {\r\n \"name\" : \"OBJECTID\", \r\n \"type\" : \"esriFieldTypeOID\", \r\n \"alias\" : \"OBJECTID\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : false, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BLDG_RECNO\", \r\n \"type\" : \"esriFieldTypeInteger\", \r\n \"alias\" : \"BLDG_RECNO\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"building\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"building\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_housenumber\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:housenumber\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_street\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:street\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 50, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_unit\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:unit\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 20, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_city\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:city\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 20, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_state\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:state\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 2, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"addr_postcode\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"addr:postcode\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 5, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"name\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"name\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 100, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"height\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"height\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 10, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Import_2_OSM\", \r\n \"type\" : \"esriFieldTypeSmallInteger\", \r\n \"alias\" : \"Import_2_OSM\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BlockGroup\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"BlockGroup\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 12, \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Shape__Area\", \r\n \"type\" : \"esriFieldTypeDouble\", \r\n \"alias\" : \"Shape__Area\", \r\n \"sqlType\" : \"sqlTypeDouble\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Shape__Length\", \r\n \"type\" : \"esriFieldTypeDouble\", \r\n \"alias\" : \"Shape__Length\", \r\n \"sqlType\" : \"sqlTypeDouble\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }\r\n ], \r\n \"indexes\" : [\r\n {\r\n \"name\" : \"PK__SARPY_CO__F4B70D8519C6541F\", \r\n \"fields\" : \"OBJECTID\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : true, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"user_58277.SARPY_COUNTY_BUILDINGS_SARPY_COUNTY_BUILDINGS_Shape_sidx\", \r\n \"fields\" : \"Shape\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"Shape__Area_Index\", \r\n \"fields\" : \"Shape__Area\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"Shape__Length_Index\", \r\n \"fields\" : \"Shape__Length\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }\r\n ], \r\n \"types\" : [\r\n {\r\n \"id\" : 1, \r\n \"name\" : \"1\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"1\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 1, \r\n \"building\" : null, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null, \r\n \"name\" : null, \r\n \"height\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }, \r\n {\r\n \"id\" : 0, \r\n \"name\" : \"0\", \r\n \"domains\" : \r\n {\r\n }, \r\n \"templates\" : [\r\n {\r\n \"name\" : \"0\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolNone\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"Import_2_OSM\" : 0, \r\n \"building\" : null, \r\n \"addr_housenumber\" : null, \r\n \"addr_street\" : null, \r\n \"addr_unit\" : null, \r\n \"addr_city\" : null, \r\n \"addr_state\" : null, \r\n \"addr_postcode\" : null, \r\n \"name\" : null, \r\n \"height\" : null\r\n }\r\n }\r\n }\r\n ]\r\n }\r\n ], \r\n \"templates\" : [], \r\n \"supportedQueryFormats\" : \"JSON, geoJSON, PBF\", \r\n \"hasStaticData\" : false, \r\n \"maxRecordCount\" : 2000, \r\n \"standardMaxRecordCount\" : 4000, \r\n \"standardMaxRecordCountNoGeometry\" : 32000, \r\n \"tileMaxRecordCount\" : 4000, \r\n \"maxRecordCountFactor\" : 1, \r\n \"capabilities\" : \"Query\"\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Sun, 17 May 2020 20:17:05 GMT", - "ETag" : "1255059862", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|e4a17b4a937bc34f9ea41a4803923e8a.3f00e850_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_24", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:53:17 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 23241b9c368643949e3bb1a1ba4e97c3.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "CepjkrEbiTvNaBkxM78Xa4pgJuM6rR5RZ84kObIKTvCRq9LWxvO__g==" - } - }, - "uuid" : "1edea050-3368-40f5-80ae-099dba138e0d", - "persistent" : true, - "scenarioName" : "scenario-1-Do88DoK2xjTUCXd1-arcgis-rest-services-Sarpy_County_NE_Buildings-FeatureServer-1", - "requiredScenarioState" : "Started", - "insertionIndex" : 38 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sioux_falls_sd_addresses_featureserver-c933f432-3e62-4fec-be6e-4f95894bd3b9.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sioux_falls_sd_addresses_featureserver-c933f432-3e62-4fec-be6e-4f95894bd3b9.json deleted file mode 100644 index 25acd220..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sioux_falls_sd_addresses_featureserver-c933f432-3e62-4fec-be6e-4f95894bd3b9.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "id" : "c933f432-3e62-4fec-be6e-4f95894bd3b9", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_sioux_falls_sd_addresses_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Sioux_Falls_SD_Addresses/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:22:08 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "c933f432-3e62-4fec-be6e-4f95894bd3b9", - "persistent" : true, - "scenarioName" : "scenario-3-Do88DoK2xjTUCXd1-arcgis-rest-services-Sioux_Falls_SD_Addresses-FeatureServer", - "requiredScenarioState" : "Started", - "insertionIndex" : 63 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sioux_falls_sd_buildings_featureserver-3f1883b1-14dc-402f-bbbe-8d5f346ffcd6.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sioux_falls_sd_buildings_featureserver-3f1883b1-14dc-402f-bbbe-8d5f346ffcd6.json deleted file mode 100644 index 3fbaca40..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_sioux_falls_sd_buildings_featureserver-3f1883b1-14dc-402f-bbbe-8d5f346ffcd6.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "id" : "3f1883b1-14dc-402f-bbbe-8d5f346ffcd6", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_sioux_falls_sd_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Sioux_Falls_SD_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:22:17 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "3f1883b1-14dc-402f-bbbe-8d5f346ffcd6", - "persistent" : true, - "scenarioName" : "scenario-4-Do88DoK2xjTUCXd1-arcgis-rest-services-Sioux_Falls_SD_Buildings-FeatureServer", - "requiredScenarioState" : "Started", - "insertionIndex" : 64 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_tacoma_wa_buildings_featureserver-d08e2666-3342-4c02-8f80-279e5910a37d.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_tacoma_wa_buildings_featureserver-d08e2666-3342-4c02-8f80-279e5910a37d.json deleted file mode 100644 index 51d800c8..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_tacoma_wa_buildings_featureserver-d08e2666-3342-4c02-8f80-279e5910a37d.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "d08e2666-3342-4c02-8f80-279e5910a37d", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_tacoma_wa_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Tacoma_WA_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:19:32 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "d08e2666-3342-4c02-8f80-279e5910a37d", - "persistent" : true, - "insertionIndex" : 53 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_tampa_fl_buildings_featureserver-204ccfc2-0167-4cd7-a8df-3f281d10d5d0.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_tampa_fl_buildings_featureserver-204ccfc2-0167-4cd7-a8df-3f281d10d5d0.json deleted file mode 100644 index c94fbcb0..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_tampa_fl_buildings_featureserver-204ccfc2-0167-4cd7-a8df-3f281d10d5d0.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "id" : "204ccfc2-0167-4cd7-a8df-3f281d10d5d0", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_tampa_fl_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Tampa_FL_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:22:52 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "204ccfc2-0167-4cd7-a8df-3f281d10d5d0", - "persistent" : true, - "scenarioName" : "scenario-8-Do88DoK2xjTUCXd1-arcgis-rest-services-Tampa_FL_Buildings-FeatureServer", - "requiredScenarioState" : "Started", - "insertionIndex" : 68 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_wb_zanzibar_buildings_featureserver-66d2a4b2-09ce-46d3-b9c9-a0c7489fefab.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_wb_zanzibar_buildings_featureserver-66d2a4b2-09ce-46d3-b9c9-a0c7489fefab.json deleted file mode 100644 index 8697b93c..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_wb_zanzibar_buildings_featureserver-66d2a4b2-09ce-46d3-b9c9-a0c7489fefab.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "66d2a4b2-09ce-46d3-b9c9-a0c7489fefab", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_wb_zanzibar_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/WB_Zanzibar_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"serviceItemId\" : \"a4aa6891126544fd8f80d72f5cd88c19\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"serviceDescription\" : \"\", \r\n \"hasVersionedData\" : false, \r\n \"supportsDisconnectedEditing\" : false, \r\n \"hasStaticData\" : false, \r\n \"hasSharedDomains\" : false, \r\n \"maxRecordCount\" : 1000, \r\n \"supportedQueryFormats\" : \"JSON\", \r\n \"supportsVCSProjection\" : false, \r\n \"capabilities\" : \"Query\", \r\n \"description\" : \"\", \r\n \"copyrightText\" : \"\", \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }, \r\n \"initialExtent\" : {\r\n \"xmin\" : 39.286003222427176, \r\n \"ymin\" : -5.74419491821114, \r\n \"xmax\" : 39.316219573065091, \r\n \"ymax\" : -5.7211504336860015, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"fullExtent\" : {\r\n \"xmin\" : 39.289190614176775, \r\n \"ymin\" : -5.7431377314019594, \r\n \"xmax\" : 39.313032181395613, \r\n \"ymax\" : -5.7222076213435695, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"allowGeometryUpdates\" : true, \r\n \"units\" : \"esriDecimalDegrees\", \r\n \"supportsAppend\" : true, \r\n \"supportsSharedDomains\" : true, \r\n \"syncEnabled\" : false, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsReturnDeleteResults\" : true, \r\n \"xssPreventionInfo\" : {\r\n \"xssPreventionEnabled\" : true, \r\n \"xssPreventionRule\" : \"InputOnly\", \r\n \"xssInputRule\" : \"rejectInvalid\"\r\n }, \r\n \"layers\" : [\r\n {\r\n \"id\" : 0, \r\n \"name\" : \"World_Bank_Zanzibar_Buildings\", \r\n \"parentLayerId\" : -1, \r\n \"defaultVisibility\" : true, \r\n \"subLayerIds\" : null, \r\n \"minScale\" : 160000, \r\n \"maxScale\" : 0, \r\n \"geometryType\" : \"esriGeometryPolygon\"\r\n }\r\n ], \r\n \"tables\" : []\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Tue, 26 May 2020 16:00:07 GMT", - "ETag" : "553716923", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|6f2c759a9404634eb5f5d9db97a1d88a.4c15eeda_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_21", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:36:56 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 4b2d2d4c49521bf403522140b78283a1.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "tAbjIS6P5FJXn_8F0IdqMmdFtd3qFlqDlbPdQ-J0nGsa90bYmYVX_Q==" - } - }, - "uuid" : "66d2a4b2-09ce-46d3-b9c9-a0c7489fefab", - "persistent" : true, - "insertionIndex" : 35 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_wb_zanzibar_buildings_featureserver_0-d64a22d3-e6e0-4dc6-ae5c-f9a2ba8f8485.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_wb_zanzibar_buildings_featureserver_0-d64a22d3-e6e0-4dc6-ae5c-f9a2ba8f8485.json deleted file mode 100644 index 46e6de31..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_wb_zanzibar_buildings_featureserver_0-d64a22d3-e6e0-4dc6-ae5c-f9a2ba8f8485.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : "d64a22d3-e6e0-4dc6-ae5c-f9a2ba8f8485", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_wb_zanzibar_buildings_featureserver_0", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/WB_Zanzibar_Buildings/FeatureServer/0", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\r\n \"currentVersion\" : 10.7, \r\n \"id\" : 0, \r\n \"name\" : \"World_Bank_Zanzibar_Buildings\", \r\n \"type\" : \"Feature Layer\", \r\n \"serviceItemId\" : \"a4aa6891126544fd8f80d72f5cd88c19\", \r\n \"isView\" : true, \r\n \"isUpdatableView\" : true, \r\n \"sourceSchemaChangesAllowed\" : true, \r\n \"displayField\" : \"id\", \r\n \"description\" : \"buildings_extract_zone001\", \r\n \"copyrightText\" : \"\", \r\n \"defaultVisibility\" : true, \r\n \"editingInfo\" : {\r\n \"lastEditDate\" : 1590508807797\r\n }, \r\n \"relationships\" : [], \r\n \"isDataVersioned\" : false, \r\n \"supportsAppend\" : true, \r\n \"supportsCalculate\" : true, \r\n \"supportsASyncCalculate\" : true, \r\n \"supportsTruncate\" : false, \r\n \"supportsAttachmentsByUploadId\" : true, \r\n \"supportsAttachmentsResizing\" : true, \r\n \"supportsRollbackOnFailureParameter\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsExceedsLimitStatistics\" : true, \r\n \"supportsAdvancedQueries\" : true, \r\n \"supportsValidateSql\" : true, \r\n \"supportsCoordinatesQuantization\" : true, \r\n \"supportsFieldDescriptionProperty\" : true, \r\n \"supportsQuantizationEditMode\" : true, \r\n \"supportsApplyEditsWithGlobalIds\" : false, \r\n \"supportsMultiScaleGeometry\" : true, \r\n \"supportsReturningQueryGeometry\" : true, \r\n \"hasGeometryProperties\" : true, \r\n \"geometryProperties\" : \r\n {\r\n \"shapeAreaFieldName\" : \"Shape__Area\", \r\n \"shapeLengthFieldName\" : \"Shape__Length\", \r\n \"units\" : \"esriDecimalDegrees\"\r\n }, \r\n \"advancedQueryCapabilities\" : {\r\n \"supportsPagination\" : true, \r\n \"supportsPaginationOnAggregatedQueries\" : true, \r\n \"supportsQueryRelatedPagination\" : true, \r\n \"supportsQueryWithDistance\" : true, \r\n \"supportsReturningQueryExtent\" : true, \r\n \"supportsStatistics\" : true, \r\n \"supportsOrderBy\" : true, \r\n \"supportsDistinct\" : true, \r\n \"supportsQueryWithResultType\" : true, \r\n \"supportsSqlExpression\" : true, \r\n \"supportsAdvancedQueryRelated\" : true, \r\n \"supportsCountDistinct\" : true, \r\n \"supportsPercentileStatistics\" : true, \r\n \"supportsLod\" : true, \r\n \"supportsQueryWithLodSR\" : false, \r\n \"supportedLodTypes\" : [\r\n \"geohash\"\r\n ], \r\n \"supportsReturningGeometryCentroid\" : true, \r\n \"supportsReturningGeometryProperties\" : true, \r\n \"supportsQueryWithDatumTransformation\" : true, \r\n \"supportsHavingClause\" : true, \r\n \"supportsOutFieldSQLExpression\" : true, \r\n \"supportsMaxRecordCountFactor\" : true, \r\n \"supportsTopFeaturesQuery\" : true, \r\n \"supportsDisjointSpatialRel\" : true, \r\n \"supportsQueryWithCacheHint\" : true\r\n }, \r\n \"useStandardizedQueries\" : true, \r\n \"geometryType\" : \"esriGeometryPolygon\", \r\n \"minScale\" : 160000, \r\n \"maxScale\" : 0, \r\n \"extent\" : {\r\n \"xmin\" : 39.289190614176775, \r\n \"ymin\" : -5.7431377314019594, \r\n \"xmax\" : 39.313032181395613, \r\n \"ymax\" : -5.7222076213435695, \r\n \"spatialReference\" : {\r\n \"wkid\" : 4326, \r\n \"latestWkid\" : 4326\r\n }\r\n }, \r\n \"drawingInfo\":{\"renderer\":{\"type\":\"simple\",\"symbol\":{\"color\":[38,115,0,255],\"outline\":{\"color\":[56,168,0,255],\"width\":0.7,\"type\":\"esriSLS\",\"style\":\"esriSLSSolid\"},\"type\":\"esriSFS\",\"style\":\"esriSFSSolid\"}},\"transparency\":0}, \r\n \"allowGeometryUpdates\" : true, \r\n \"hasAttachments\" : false, \r\n \"viewSourceHasAttachments\" : false, \r\n \"htmlPopupType\" : \"esriServerHTMLPopupTypeAsHTMLText\", \r\n \"hasMetadata\" : true, \r\n \"hasM\" : false, \r\n \"hasZ\" : false, \r\n \"objectIdField\" : \"FID\", \r\n \"uniqueIdField\" : \r\n {\r\n \"name\" : \"FID\", \r\n \"isSystemMaintained\" : true\r\n }, \r\n \"globalIdField\" : \"\", \r\n \"typeIdField\" : \"\", \r\n \"fields\" : [\r\n {\r\n \"name\" : \"FID\", \r\n \"type\" : \"esriFieldTypeOID\", \r\n \"alias\" : \"FID\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : false, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"id\", \r\n \"type\" : \"esriFieldTypeInteger\", \r\n \"alias\" : \"id\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"building\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"building\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 254, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"problemati\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"problematic\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 254, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"changeset\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"changeset\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 254, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"area\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"area\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 254, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"condition\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"condition\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 254, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"zone\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"zone\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 254, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"BRN\", \r\n \"type\" : \"esriFieldTypeString\", \r\n \"alias\" : \"BRN\", \r\n \"sqlType\" : \"sqlTypeOther\", \r\n \"length\" : 254, \r\n \"nullable\" : true, \r\n \"editable\" : true, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Shape__Area\", \r\n \"type\" : \"esriFieldTypeDouble\", \r\n \"alias\" : \"Shape__Area\", \r\n \"sqlType\" : \"sqlTypeDouble\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }, \r\n {\r\n \"name\" : \"Shape__Length\", \r\n \"type\" : \"esriFieldTypeDouble\", \r\n \"alias\" : \"Shape__Length\", \r\n \"sqlType\" : \"sqlTypeDouble\", \r\n \"nullable\" : true, \r\n \"editable\" : false, \r\n \"domain\" : null, \r\n \"defaultValue\" : null\r\n }\r\n ], \r\n \"indexes\" : [\r\n {\r\n \"name\" : \"PK__WORLD_BA__C1BEA5A209708824\", \r\n \"fields\" : \"FID\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : true, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"user_58277.WORLD_BANK_ZANZIBAR_WORLD_BANK_ZANZIBAR_Shape_sidx\", \r\n \"fields\" : \"Shape\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"Shape__Area_Index\", \r\n \"fields\" : \"Shape__Area\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }, \r\n {\r\n \"name\" : \"Shape__Length_Index\", \r\n \"fields\" : \"Shape__Length\", \r\n \"isAscending\" : true, \r\n \"isUnique\" : false, \r\n \"description\" : \"\"\r\n }\r\n ], \r\n \"types\" : [], \r\n \"templates\" : [\r\n {\r\n \"name\" : \"world_bank_zanzibar\", \r\n \"description\" : \"\", \r\n \"drawingTool\" : \"esriFeatureEditToolPolygon\", \r\n \"prototype\" : {\r\n \"attributes\" : {\r\n \"zone\" : null, \r\n \"area\" : null, \r\n \"condition\" : null, \r\n \"building\" : null, \r\n \"problemati\" : null, \r\n \"changeset\" : null\r\n }\r\n }\r\n }\r\n ], \r\n \"supportedQueryFormats\" : \"JSON, geoJSON, PBF\", \r\n \"hasStaticData\" : false, \r\n \"maxRecordCount\" : 2000, \r\n \"standardMaxRecordCount\" : 4000, \r\n \"standardMaxRecordCountNoGeometry\" : 32000, \r\n \"tileMaxRecordCount\" : 4000, \r\n \"maxRecordCountFactor\" : 1, \r\n \"capabilities\" : \"Query\"\r\n}", - "headers" : { - "Content-Type" : "text/plain; charset=utf-8", - "Cache-Control" : "public, max-age=30, s-maxage=30", - "Last-Modified" : "Tue, 26 May 2020 16:00:07 GMT", - "ETag" : "146378908", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Correlation-Id" : "|67c68cc4ab368f49a6a9a8179004afcc.d3385f51_", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_12", - "X-Powered-By" : "ASP.NET", - "Date" : "Mon, 01 Jun 2020 16:51:18 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 045f1e1f031241f3808c557a2b5d6b0a.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "OpPAoWtqjb3vnAxpxvNnx249RL0i2ri1HoqdVCmkbK0QX5eH8t--6g==" - } - }, - "uuid" : "d64a22d3-e6e0-4dc6-ae5c-f9a2ba8f8485", - "persistent" : true, - "insertionIndex" : 34 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_williamson_county_tx_buildings_featureserver-6db57544-0cfa-4b2f-8cd3-5845cac15108.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_williamson_county_tx_buildings_featureserver-6db57544-0cfa-4b2f-8cd3-5845cac15108.json deleted file mode 100644 index 2b8aa8c0..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_williamson_county_tx_buildings_featureserver-6db57544-0cfa-4b2f-8cd3-5845cac15108.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "6db57544-0cfa-4b2f-8cd3-5845cac15108", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_williamson_county_tx_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Williamson_County_TX_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:19:44 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "6db57544-0cfa-4b2f-8cd3-5845cac15108", - "persistent" : true, - "insertionIndex" : 54 -} diff --git a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_yavapai_county_az_buildings_featureserver-037b364c-e9e2-4478-8bd9-8bd9a3b3c9aa.json b/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_yavapai_county_az_buildings_featureserver-037b364c-e9e2-4478-8bd9-8bd9a3b3c9aa.json deleted file mode 100644 index a885a443..00000000 --- a/src/test/resources/mappings/do88dok2xjtucxd1_arcgis_rest_services_yavapai_county_az_buildings_featureserver-037b364c-e9e2-4478-8bd9-8bd9a3b3c9aa.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "037b364c-e9e2-4478-8bd9-8bd9a3b3c9aa", - "name" : "do88dok2xjtucxd1_arcgis_rest_services_yavapai_county_az_buildings_featureserver", - "request" : { - "urlPath": "/Do88DoK2xjTUCXd1/arcgis/rest/services/Yavapai_County_AZ_Buildings/FeatureServer", - "queryParameters": { - "f": { "equalTo": "json" } - }, - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n

\r\n
\r\n
\r\n
\r\n
\r\n

\r\n

\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n", - "headers" : { - "Date" : "Tue, 12 Jan 2021 16:04:32 GMT", - "Content-Type" : "text/html", - "Vary" : "Accept-Encoding", - "Last-Modified" : "Fri, 18 Dec 2020 00:52:32 GMT", - "ETag" : "\"d00540382ee89141e2515dddd6cba757\"", - "Server" : "AmazonS3" - } - }, - "uuid" : "037b364c-e9e2-4478-8bd9-8bd9a3b3c9aa", - "persistent" : true, - "insertionIndex" : 46 -} diff --git a/src/test/resources/mappings/faviconico-0a890cfc-c50c-44be-973b-acf92836e513.json b/src/test/resources/mappings/faviconico-0a890cfc-c50c-44be-973b-acf92836e513.json deleted file mode 100644 index e49ea440..00000000 --- a/src/test/resources/mappings/faviconico-0a890cfc-c50c-44be-973b-acf92836e513.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "id" : "0a890cfc-c50c-44be-973b-acf92836e513", - "name" : "faviconico", - "request" : { - "url" : "/favicon.ico", - "method" : "GET" - }, - "response" : { - "status" : 200, - "base64Body" : "AAABAAMAEBAAAAEAIABoBAAANgAAABgYAAABACAAiAkAAJ4EAAAgIAAAAQAgAKgQAAAmDgAAKAAAABAAAAAgAAAAAQAgAAAAAABABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAoCQkJMUjIyPxUlJS7SoqKtgAAACsAAAATgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQKCgqtfXhx4W2Fbe8TFAv/pm4f/8CCK/9LOB3/w6Fx6mZiXOYFBQWyAAAAFAAAAAAAAAAAAAAAAAAAABIcHBzHxK6P6JBpJ/8iRyb/Yo5D/1w6C/85JAf/e04P/715GP9VZi7/naud3CYmJq4AAAAMAAAAAAAAAAAKCgqqsJ+G61I1Dv8lPiH/S6Ng/1ihW/+eZRT/UTQK/2pDDf9QOg//GTMa/ypOLP98hnbrEhIShwAAAAAAAABHfHl065+OQP8gLhj/Gjok/z6NW/9KrHH/OTMT/7Z0F/++eRj/qYAj/yxcMv9GkEv/nZhM/1dVU+sAAAAoBAQEqNKyh/RMWSj/MnRL/0iocf8jVTz/FTQk/2pJGv/BgCP/vnkY/7p6Gv8kTS3/TKFa/4yJM/9+aUzwBwcHhzc3N9xbTDb+MCEK/5mfW/+bq27/u6tu/1FAJv9mTiz/RTEU/00yC/9KRhv/ECUX/yNMLf8qTCn/aV1L/kJCQsNZWVnyi4dQ/ykeDv9mTi3/1qx1/7+ecv9/aEr/2q1v/6qjYP+ImVX/Po9h/yZYOv9Lqmv/S6Zj/3F0Uf9dXV3rY2Nj5kxzWf+ffEL/rolX/09CMf9DOi7/zrKN/8m5hv9JuYv/SbiI/yxtT/88j2P/S61z/0qmaP9cfGL/V1dX9CoqKsIvOjP/Xa95/6K1f//lwZL/VUs9/2FUQ/9gVD3/MH5i/zuZdf8ZPy//PJRq/zF1UP8dQiv/ZXtr/zQ0NNoVFRWFNjs47ytpSv9JuYv/obGD/4VwVP/jvYv/1Ktx/0WVdv8jXkz/GkU1/zF7W/8+l2v/KF4//7PLvP0BAQGaAAAALkRERPZgl3b/Hkgz/yhhSf+hkmH/2apq/9OgWv/ElUj/OUQq/0S0jv9Juo3/OY5m/2eVe/9eXl77AAAALAAAAAAUFBSIy9fP7F60f/8jUjb/I081/1RTMP+QZy7/eVId/2hCDf9skFf/OJFv/xk0KP87RD//AAAAhgAAAAAAAAAAAAAABywsLKzb29Hof3NM/56MOP+Eij3/Y0QR/z0nB/9wRw7/fE8Q/35tVP97eXb+BwcHjAAAAAEAAAAAAAAAAAAAAAAAAAAIGhoahmNjY9/w4cvum31T/1BFN/++k1b/poRU/3ltW/s0NDS8BAQEPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuHh4edFdXV6ZGRka9QEBAoktLS056enoZAAAAAAAAAAAAAAAAAAAAAAAAAADwB///wAP//4AB//+AAf//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//+AAf//gAH//8AH///wH///KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1AAAAgwAAALoAAADeAQEB9AYGBvUBAQHyAAAA1AAAAJkAAABFAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAModHR3iOjo67z8/P+Q3LBzvs5Zv+uPGnfHm0K/kQD896UpKSuYiIiLXAAAA1gAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAACJERER6ainpdLF0KzySWdL/wkTCv9NMQn/t3UX/755GP97Tg//NyMH/8qUSf/mzanyamhn4gsLC+YAAACMAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAKEzMzPf5NbD48mQQP8/RBr/Gjcd/0ubUf9uTxT/JhgE/29GDv8eEwP/r28W/755GP+0exz/PFcv/7O3s9tISEi7AAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfzIyMuKzq5/tvn0i/2A9DP8ZNR3/S59Z/0yiW/+rfyP/rW4V/xELAv8YDwP/SC0J/3VKDv9qayf/IkYk/0BsQv+rs6zoExMT2QAAAGUAAAAAAAAAAAAAAAAAAAA5ERER7ePXxehxTBj/IxYE/xEmFv9JoF7/TKZk/0ynZf+PjDj/eEwP/0EpCP+6dhf/kl0S/2xFDf8tQx//Dh0P/x08H/82aTn/2dPJ3x0dHcoAAAAbAAAAAAAAAAAAAADMo6Oi4MiWSf9zdC7/CQ8I/wsYD/86glH/S6pt/0urb/9clVb/HRID/65uFv++eRj/vnkY/755GP9imEr/Hj8h/0GFRf9NnVH/yqNh/3Z2du0AAACZAAAAAAAAAEEeHh7u6NCu9oKPPP8VLRr/L2tD/z6NW/8OIhb/H0gw/0Ofbf8fSjP/c0sU/756Gv++eRj/vnkY/755GP90lET/FCsY/0mZU/9NnlP/unoZ/31tV/koKCjYAAAAFAAAAJV+fn7sz5xU/2tyMP8KGA//Sqpv/0qvd/9Jrnn/J2FG/wYQDP8HBwT/onAr/8SEKv+/fBz/vnkY/755GP+CkD//DyAT/0yjXf9MoFj/pYEl/2BAE/9qamrlAAAAZwAAAM6EhITxPSgJ/z4oCf9VVSz/d6do/3atcv+TrHH/tKlr/1tIKv9GNR7/HBUL/yQZCf9XOhH/eE0Q/4ZdF/84ckX/DyIV/y9nPf8iSSn/HiUQ/x8VB/+xsbHqAAAArAAAAO/l2MTzXEQU/wAAAP97Wiv/059Y/9ioZ//br3L/1610/yMcEv/Tpmn/1KJe/6t+Qf94VST/VjoS/x8+KP8MHhT/FTEf/ypeO/85fkz/S55b/0o/Ff/17ePvAgIC2QsLC/Pq2r/6UG88/zIkEf8sIRH/UD4m/82lcP/ivIj/m4Jh/2VUPf/gt4D/2qxv/6Coav+Epmb/aKls/0qxe/8WNST/P5Ri/0urbv9LqGn/TKZj/zJEIf/z59b5CwsL7REREebR6Nf6IEsw/4JfLv/Rn1v/emFA/xwXEf+AbVT/Rz4x/7uhgP/lwJD/vraA/0q5iv9KuIj/SraF/0asfP8MHhX/SrB5/0uuc/9Lq27/S6ho/x9FKP/l7uP6CQkJ9gsLC8/a59/zECUX/3WUXP/TpGH/3bJ3/9Cugv9iVUT/AAAA/3JkUv/Kq4T/wbmF/0m8kf9Ju4//SbqM/y93WP8hUjv/SrN+/0qweP9LrXL/P45b/xg1IP+2urf3AAAA6wAAALI9Pj3kCxgR/0qzfv9QuIj/nbeE/+O+jP+xmHn/YlZH/4p3YP82LiL/LCYb/yBWQ/8ueF7/Noxs/xEtIv8tcFP/NH9c/yliRP8ZOyf/BxIL/052W/+5ubn5AAAAwgAAAHZ+fn7MAgYE/zJ5VP9Kt4b/SbyQ/667if9wXkb/tJl2/+bCk//iuob/yqRs/zaKcf8naFT/Fz0x/wgXEv8eTDr/J2FI/zSAW/9Eom//GTom/6jWuP9sbGz7AAAAfwAAACIeHh7bOUI8/x09Kv8cRDH/RKl+/1O7jv8/PSv/3rV//9+1ff/cr3P/16Zk/6WpbP9cvJL/HlJD/y56YP9JvJD/SbmK/0q1g/8ubk3/OndT/+Hp5P4QEBD6AAAAIgAAAAAAAAChZ2dn+p/Ssv8zd1D/DiIY/yVdRf8sRTH/xatt/9enZv/Vo1//0ZxS/8yTQ/+pdSz/Gx0P/0i+l/9JvZP/SbqM/0SqfP8YOij/sNvD/2JiYv4AAACbAAAAAAAAAAAAAAAaHh4e1vT29et6wZT/Sapx/ylhQf8HEgz/HTwq/2htQv+zjEf/ypA+/8OGMP8uHwr/j1sS/3yhYf9JvpT/SbmM/xg8LP8wW0b/Nzc3/wAAAOcAAAAUAAAAAAAAAAAAAAAAAAAAUkdHR9v1+fbykLyM/1R/Sf9CcUX/Q49b/yhbOv8cLhr/KRsI/yAVBv8QCgL/SC4J/0IqCP8nOSP/DhsS/11YUf92dnb/CQkJ8QAAADcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1MTEzU/Pv66J6Od/+daiH/vYAj/6aGLv+wgCT/Xz0N/zUiBv+nahX/qWwV/7ZzFv+4fiz/tKqb/359ff8EBATaAAAALgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYKCgoyGdnZ9bf29b75Mih/8iYVP9DKwj/MB8H/7d2Gv/FiDP/0qNi/8Kohf9+fHn/Gxsb8AMDA4IAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAAAKZDQ0PAycnJutTU1NRISEjyjo6O/IGBgfxjY2P0Nzc33RoaGrAgICBmT09PEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAcAsLC5olJSWiPz8/oE9PT4Z/f386////Df///wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AP//4AB//4AAH/8AAB//AAAP/gAAB/4AAAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/gAAB/4AAAf/AAAP/4AAH//AAD//4AD///gD//ygAAAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAAAGYAAACcAAAAxAAAAOEAAAD2AAAA/wAAAPoAAADlAAAAvwAAAIQAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAMEAAAD+AAAA/jExMd9SUlLWCwsL+4SEhPS4uLjgqqqq3IqKitQeHh7oAQEB+QAAAPsAAADUAAAAYwAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJgAAAMIBAQH6QEBAzcXFxct5gnn0ExMT/x8VCP+hbST/xok0/8aKNf/LlEf/q4xh/wcGBf+knJLx2travEtLS74DAwP1AAAA0QAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFEAAADzICAg2tDQ0Mbq2Lz3dqx1/xYuGP8OHQ//DAsE/3VKDv+9eBf/vnkY/7NyFv8ZEAP/YT4M/756Gf/RoFz/8OLM91JSUu0UFBTTAAAA9AAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABeAAAA/FhYWMn27+bk0qJf/5Z5I/8QIhH/Fy8Y/0qYT/9rcy3/IhYE/y0dBf+SXRL/SC4J/zEfBv+8eBf/vnkY/755GP+kgSb/LkUt/66uruKBgYGdAgIC7wAAAE8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAAAAPtwcHDH8uXS88WHMf+zchb/JhkF/xcwGv9LnFX/TKBY/4WMOP+6dhf/ZkEM/wsHAf8AAAD/WDgL/6hrFf++eRj/vXkY/2SVRf8ePR//VX5X/+rz6vOsrKyUAwMD6AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0AAADtV1dX0KyppvRWPRr/sHAW/zAeBv8SKBb/SZ1Z/0yjXv9MpF//dpRF/755GP+ycRb/FAwC/0ctCf8vHgb/BAIA/xcPA/83JQj/IEEi/w0aDf8XMBn/NEs2/0ZGRvYrKyvUAAAA1QAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAuiAgIOT28erowYc0/zUiBv8LBwH/ChcN/0aZWv9MpmP/TKdl/0ynZv9VpGH/u3oZ/0wwCf8uHQX/vHgX/755GP+2dBf/lV8S/19XHv8rWC3/CxgM/yJGJP87eT7/f6ds//37+eU2Nja6AAAAgwAAAAAAAAAAAAAAAAAAAFIAAAD9yMjI1tWqbP+tfiH/UVAf/wECAf8DCAX/M3FF/0upav9Lqmz/S6tu/0urbv9xayv/BwQA/6FnFP++eRj/vnkY/755GP++eRj/jYky/02fVf8QIhL/PHs//02dUf9plEL/3ryM/5KSku4BAQHwAAAAGQAAAAAAAAAAAAAAzUBAQN/x4s76rX8i/0ySUf8ECQX/KVw5/y5nQf8DBgT/HEIq/0Wgav9LrnX/Sq10/w4gFf9fPQ3/vnkY/755GP++eRj/vnkY/755GP+bhSz/TKFa/wcPCP9EjUv/TZ1S/22SQP/AfSD/nJWL/yQkJNEAAACHAAAAAAAAADEAAAD/u7u74dOlZP+Bjz3/Ikor/woWDv9Jp2r/S6xx/z+UY/8RKRz/BAsH/yVZPf8qZEb/Fg8F/7x+J//BfiD/vnkZ/755GP++eRj/vnkY/7R8Hf9HmVn/AgUD/0ufWf9Mn1b/apRE/755GP+CXiv/g4OD0QAAAOgAAAAEAAAAfQ4ODvT59O7yv3wd/4qCMf8DCAX/LGRB/0utcv9Lr3f/SrF7/0myfv8tcFH/BxMO/wAAAP8vIQ7/p3Uu/8WHLv/CgCP/vnoZ/755GP++eRj/t3sc/zl5SP8OHhL/TKNe/0yiW/9SnlX/uHob/3NJDv+BgYHxExMT7QAAAEIAAAC2S0tL7GFfXP5iPgz/bUcR/woHAv9ym1v/VK50/1Oxev9tr3f/jK1y/66pav+Gaz7/Ew4I/yEZDv8AAAD/JhsK/15AFf+IWRf/pWkV/7l2F/9um1L/JlU1/xw9Jf9BjlX/NXJC/yVPLP8gHgv/BgQA/4GAgP5UVFTjAAAAhwAAAN6Dg4PvnIlv/wsHAf8CAQD/STQW/82VR//RnVT/1aNf/9ioZ//Zq23/2qxv/zgsHP9zWjf/1aNg/6R8RP9iSCT/LSAO/wgFAv8AAAD/AAAA/wMHBP8BAwH/AAAA/wQJBf8SJxj/I00t/0B3QP9mRA//tqye/4+Pj+UAAAC8AAAA9Kurq+7Up2j/c2wt/wAAAP8HBQL/e1wx/9WkYP/Zq23/3bF3/9+1fv/DoHD/BgUD/8ukb//arG//1qVj/9KdVf/NlUb/x4s3/6J8Lv9Ejl3/OIJY/wACAf8/kV7/S6lr/0uoaP9MpmT/TKRg/y9JJf+6o4L/tLS06AAAAOIAAAD8xMTE67qrbv8tXzz/IBcK/31cLv8KCAT/PC8d/8Cbaf/iuob/5L+P/3ZjS/9JPS7/4ruI/960fP/arG7/mKtv/3Gtc/9orHH/TrJ8/0qyff8zeVT/DR8V/0utc/9LrG//S6pr/0uoZ/9MpmP/Gjgh/8Orff/Dw8PrAAAA9wAAAPXQ0NDlhMOY/xUyIP9fRSH/0ZxT/72SWP9ENiP/BwUE/2taRP/au5T/JiEa/56IbP/mw5X/4ruH/7e0ff9KuIn/SriI/0q3hv9KtoT/SrSB/xtDL/8iUjn/S7B3/0uuc/9LrG//S6lr/0ynZv8RJhf/oMqj/729ve8AAAD9AAAA4czMzNyTy6f/BAoH/3pzQP/Snlb/2Kho/96zef+jh2P/KCIa/wsKCP8BAQH/y7OU/+rJnv/kv47/p7iF/0m7j/9Juo3/SbmL/0q4if9IsoP/BAwI/zmLYv9KsXv/S693/0utc/9Lq27/Rp1i/xMqGf+t1bb/pqam8gAAAPEAAADAZWVl5565p/8AAQH/S6h1/5Sqbf/CrG//3rN6/+S+jP/nxpz/YVVF/w8NC/8EBAP/S0Az/5uCYf+/r3z/Sb6U/0m9k/9JvJD/SbuO/zJ+Xv8IFRD/SbSC/0qzf/9KsXr/S692/0aja/8jUTP/CBML/15kYP97e3v1AAAA1QAAAJEuLi7QEhMT/wsbEv9Ksnz/SraF/0q7jv+cuIb/4ruI/+fFmP9GPTD/n4xz/8atjP9uXkj/Ix0V/wQEA/8QLCP/H1JA/yptVf8xf2L/Ei4j/xc7LP8zflz/K2pM/yBONv8RKBv/BxAK/wMHBP85eU7/9Pn1/0BAQPgAAACmAAAAVTIyMsRoaGj2BQsH/zmHXv9KtYL/SbmL/0m9k/+wuon/4LuK/xMQDP/evpT/58WY/+S/jv/guIH/vZpm/y9+Z/8hWUj/Fjww/wsdF/8AAQD/DCAZ/xMwJP8dSTX/KmhK/zuNYv9BmGf/ECYZ/3/Clv/w8PD7BgYG/QAAAGIAAAAQBAQE61paWsoZJh7/BQwI/yVbQf9Is4P/SbqN/1O8kf+gkWb/QDUm/+O8if/iu4f/4LeA/9ywdf/YqWj/freH/0jEov9Iw57/IltJ/xU3K/9JvJH/SbqN/0q4iP9KtYP/SrN9/x9LM/8vbkj/y+bV/5WVlfsAAAD2AAAAEgAAAAAAAACaBwcH7nl8ev9Xn3P/ESgb/wwfFv82h2P/SbmL/zFsUf96YkH/3bJ3/9ywdf/arXD/2Khn/9ShXP/Qmk//q55Z/2akdf8ECgj/N5F0/0m+lP9Ju4//SrmK/0q2hP8/mm3/DR8V/3S+k//09PT+HR0d/gAAAJgAAAAAAAAAAAAAAB8AAAD2i4uL987o1/9LrHL/LWtJ/wcQC/8TMCP/Ey8j/2GNYv/LqWn/16Zk/9WjX//Tn1j/0JlO/8yTQ//Iizb/UDYS/yksF/9JwZr/Sb+W/0m8kf9JuYv/SbaF/xQxI/89hF7/4PHo/2NjY/8AAADyAAAAGgAAAAAAAAAAAAAAAAAAAH44ODjU////76bVtv9LrXH/R6dx/yhfQf8DCQb/ChoT/y5bQP9+i1b/v5xU/86WSP/LkUD/x4s1/5FiIP8JBQH/pGgU/3yhYf9Jv5f/SbyS/0m6jP8oZEr/HEQw/2OJdv82Njb/AAAA/wAAAG8AAAAAAAAAAAAAAAAAAAAAAAAABQAAAMl6enrI/f39+pvPrf9Lq27/S61y/xUyIf83gVb/HUQt/wcQCv8KEwz/QjAV/2ZGGv95Uhr/EQsD/1M1Cv+gZhT/mWMV/1N9Uf8ueF3/IVVA/wACAf8iJSP/UFBQ/wEBAf8AAACqAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAEBAeKZmZnD////+tLPrP+djj7/MDAW/4qXS/9vn1n/Uqdn/0KLVf9nUh3/SjAL/wAAAP8WDgL/IRUE/yUXBP8xHwb/RSwI/zgkB/8oHxL/wrms/7u7u/8SEhL/AAAArgAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgEBAeKMjIy5////8NrRxP9WPBn/wHwd/8B9Hv+/fR7/vXwc/7BxF/8rGwX/KBkF/7VzFv++eRj/vnkY/755GP++eRn/uJRj/+Lc1P9ubm7/AgIC+wAAAIEAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAM9PT0+2vb29yoKCgv/x4s3/1650/8SGL/+cYxP/GhED/yIVBP+vbxb/vnkY/755GP/Gizf/27R//9HGt/94eHj/EBAQ/wMDA8QAAAAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAIsFBQXlT09Pzv39/cP////3/v7+/w8ODf83Nzf/y7mf/863mP+8q5P/oJqR/3Nzc/8wMDD/BAQE8RMTE60kJCRF////AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMAAACVCwsL0EdHR6uwsLCisLCwpoKCgsFkZGTOWVlZzk1NTcFDQ0OpSEhIhl1dXU/a2toV////AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAATwAAAJQCAgK+DQ0NuhkZGawhISGSJSUlZv///wn///8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8AD//8AAH/8AAA/+AAAH/AAAA/gAAAHwAAAA8AAAAOAAAABgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAGAAAABwAAAA8AAAAPgAAAH8AAAD/gAAD/8AAB//wAB///AD/8=", - "headers" : { - "Date" : "Mon, 01 Jun 2020 16:32:05 GMT", - "Content-Type" : "image/vnd.microsoft.icon", - "Last-Modified" : "Sat, 21 Mar 2020 00:25:43 GMT", - "ETag" : "\"123eec196590856429c0ddb8e8fd6e34\"", - "Server" : "AmazonS3", - "X-Cached" : "HIT", - "Link" : "; rel=\"canonical\"" - } - }, - "uuid" : "0a890cfc-c50c-44be-973b-acf92836e513", - "persistent" : true, - "insertionIndex" : 23 -} \ No newline at end of file diff --git a/src/test/resources/mappings/faviconico-b9f04630-0820-4f01-a541-b9d4226b8f84.json b/src/test/resources/mappings/faviconico-b9f04630-0820-4f01-a541-b9d4226b8f84.json deleted file mode 100644 index c8316ffa..00000000 --- a/src/test/resources/mappings/faviconico-b9f04630-0820-4f01-a541-b9d4226b8f84.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "id" : "b9f04630-0820-4f01-a541-b9d4226b8f84", - "name" : "faviconico", - "request" : { - "url" : "/favicon.ico", - "method" : "GET" - }, - "response" : { - "status" : 200, - "base64Body" : "AAABAAIAICAAAAEAIACoEAAAJgAAABAQAAABACAAaAQAAM4QAAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAEEAAABAAAAANAAAACcAAAAaAAAAEQAAAAgAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACEFBQVWFxcZqxQUFZcGBgZsAAAAVQAAAFIAAABNAAAASAAAAEEAAAA2AAAAKAAAAB8AAAATAAAADQAAAAYAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAiAAAARTIxM7x5eHn/amhq/zQ0Nv8tLC3eKSkqpxcWFm8AAABNAAAATQAAAE0AAABMAAAASgAAAEcAAABDAAAAPgAAADQAAAAjAAAADwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAGgAAAEAZFxlog4GB////////////8/Py/769vf+CgX//WllX/0VEQ+hFQ0O6NjQzgAkJCVAAAABEAAAARAAAAEQAAABEAAAARAAAADMAAAAVAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAABgAAAA1AAAAO0dFRc7o5+f/5eXl/6Gho/+wsLH/5eXl///////39/f/2tnZ/6Wkov9+fHr/VlRT/0hGRcM5NjaSFRQUVAAAADsAAAAzAAAAEgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAbAAAALwAAADMlJCZmk5GR//////+Wlpj/9PLr/+Lh1/+/v8D/n56Z/6yrp//a2tr////////////m5eX/u7q6/4GAf/9WVVT/Pz4+5Tk2OJg2NTVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAHQAAACoAAAArAAAAK1RSUuXw7+//zc3N/5mOXv/OxqD/6ObX//////+5rnL/5OHQ/9bW1f+pqav/pKOf/8XFxf/09PT///////f39//Nzcz/kpCQ/1JRUf8yMTLvMTAyEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAABsAAAAiAAAAIwAAACM0MzN2rKqq//////+ek2r/179M/8qyO/+4oRb/rp5I/6OVP//Qy63/+/r3//f28f+5rnL/7+/s/7q6uv+6urr/u7u7/+np6f//////+/v7/+Df3/98enpAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAUAAAAGwAAABsAAAAbFBMTKWdmZuP4+Pj/w8G9/7yoQf/UvU//zLZN/+HIS/+plQD/xrA4/7WeGv+plST/oZRC/7uxgP/Qy7H/5OLV/87Gn//X0bX/z87L/52ZiP/U1NT/9/f278LAvSAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAA8AAAASAAAAEwAAABM+Oz2KwsHB//Pz8/+6urr/49/K/8C3gv+6qlT/rpoU/7eiJ/92az3/SEMu/3ptM/+LehX/s50d/6eRAP+omEP/pphQ/8zFpP/Lw57/qKSO//Hw8P+zsrGFAAAAEwAAAAwAAAAFAAAAAQAAAAAAAAAAAAAAAQAAAAMAAAADOTc4E2xpa/D7+/v/rayu/+Xl5f/Uz7X/9/by//n38v+3rXL/q6OE/3d1dv+7urr/eHh4/0BAQP9BPzb/a2I8/4FyJP+ymx3/pY0A/4ZzCP/W1tb/09LR0gAAAEgAAABGAAAAQQAAADUAAAAeAAAABAAAAAAAAAAAAAAAAAAAAABJR0ef1tbV//X19f+IfUv/koM3/7uub//PxqH/ua1z/+He0P9nZFv/6Ojo/9fX2P/x8fP/9/f3/9fX1v+cm5r/ZWNi/01KSv9tZEP/hX1j/9nY2PNEQ0JZAAAAQgAAAEIAAABCAAAAOAAAABcAAAABAAAAAAAAAAAAAAAAUU5OIIKAgP//////pqOW/6OVV/+BdT3/tqZY/8u3UP+xnSv/hXg5/6empv/y8vL/e1IA/4NRAP+Iemf/pqam/+Xl5f//////5eXk/7e2tf+Ih4b/WlhX/0pHR9o9OzupHh0eXgAAAC0AAAASAAAAAQAAAAAAAAAAAAAAAAAAAABeXFy/5ubk/+zq7P+XilD/Uk4v/0BIRf+Jezj/oZAq/8GwYP9vbGf/9/f3/4R/eP+5ZwD/7YoA//iXAP/gjwD/o3AA/5l+Tv+Qj5H/29vb///////09PL/y8rK/4uKiP9cWlr/Pj097zg3OZ8zMjNQAAAAAAAAAAAAAAAAZmRiQKGfn///////sauV/9HDgv8CNlv/ADtk/2JZI/91aiH/WlIx/7q5uf/Hx8f/d0cA/51ZAP/UggD/+KAA//mlAP/6qQD/+qwA/+6mAP/AiQD/knI0/5aPh//Gxsb/8/Pz//j4+P/Z2Nj/m5qa/2JhYf8xMDGAAAAAAAAAAAB7eXjP6+vr/9ra2v/Cu5r/QUxP/wBqt/8AZaf/NTo4/2hfN/9OTU3/pKSk/1xKLP9+SQD/TjkS/6tuAP/1pAD/+64A//u0AP/8twD//LcA//u0AP/7rgD/+aYA/9iPAP+VagD/pJBx/7Kxs//q6ur//////7q4uJ8AAAAAZ2VjYLi2tP//////q6eX/62vrv8ARnj/AJH1/wCS6v8AWI3/HSMs/42Njf9nZ2f/gEgA/zU0Lv8Aa6P/c1IA/+SeAP/7tgD//L0R//3ALf/9wC7//b0S//u1AP/6rQD/xZYA/4yNAP99fQD/QlsA/19kXf/39/f/0tHOQF1bWhB5d3Xf+Pj3/9TSzP/ezXz/gnxW/wBlq/8Aof//AK7//wCm8P8CZpz/P09i/1w8AP9mPgD/AGmd/wC5+v8lNkX/zpMA//y9Ef/nvTD/wcRc/7fFZP+2wVr/tbY//3qtUv9DrnL/QJpR/zZ5Kf82VCf/4ODg/+Lh4IAAAAAAWVdWgMfGxP/4+Pj/qZ5v/9TCb//Ov3j/Gkhn/wCo//8AuP//AMX//wC//P8AkdT/Dk12/xg/WP8ArPD/AMv//wBajf+tgRr/9L0i/2fZo/87tZb/LIpu/0PLqf9K163/R8OQ/0Svcf89k0//MmMp/62vrP/p6OfPAAAAAAAAAAB4dXXv/Pv7/8zLyP/p5tX/4NvD/8i+kv+Nim//AGeY/wDD//8A0P//ANz//wDa//8Aq+z/AJrf/wDM//8Axv//AJzl/1BTO/920o3/SNy6/xR3jv8byv//Fnh5/ybFlP8xtoD/Oqdp/x5uMv+AiYD/8/Py78XDwCAAAAAAAAAAANXU1P//////vbqx/7Otk/+9tpf/z8ac/9PMqP9+fnT/AG2Z/wDW//8A3///ANn//wDT//8Azv//AMf//wDB//8Atf//BD5l/0bRtf9M6MX/MqOT/yCSiv8QrH//L8GP/zGxd/8dgkf/TW1W//Ly8f/S0M5QAAAAAAAAAAAAAAAA6ejmv/Hx8P/8+/v///////Hx8f/j4uL/1tPP/3t5c/+mqa//HFh0/wCr3P8A0v//AMD//wDI//8Awv//ALv//wC1//8Ad8L/NJSI/03pxv9E5cD/J9Sn/0XSpf9Hw5D/PJ9q/z5uS//b29v/4+LhnwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMnHxDDU0tBQ5eTjgOrp6Z+3trbfs7Kw//X19f+MZhf/SjgT/xaCtf8AuP3/AMP//wC9//8Asv//AIDK/wBGc/84oJL/TObD/yeNef8UeX//MYtl/0KyfP8vcEX/rrCu/+3t7M/HxcIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaWdnEIqIhu/4+Pf/qJ6P/51ZAP8fQlj/Cazv/wDF//8Aktf/AFaF/ydhZ/85mYj/SNSw/0retv9J1av/G4Oc/zPT//8VbWj/M4ZT/3uMf//39/fvxsTCMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABnZGSA0tHR/9zb2/+LVgD/ZEQA/wB4qf8AZpP/GU1a/zN1Y/9Ls47/VtCm/0jNoP86xpT/PsST/zm6if8njV7/KXNI/zKFUP9YeGD/7Ozs/9/e3XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXlxaIJeVk///////mINn/5dVAP9dWAD/LFg8/0GNVP8ze0v/EH+Z/xdsav9DsYL/TcGQ/zizef8RbVv/Anqf/yJzVP86mFj/O2xE/9LR0f/m5eSvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABiYGC/6urp/8fCu/+TUwD/hVUA/2RdAP8+fyz/RI08/yFTKv86zv//FHOF/z6dZ/9QtH7/R6ps/xp1gv80wvH/F2BO/yJiLP+prqj/7+7u38jFwxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUE5OMKmnp//4+Pj/eF81/zEzAP8eRRP/KFYd/zNrJv88eyv/PIE0/zZ4Of85iUf/SJ9c/02kZP9MpWT/OIE0/zFzKP8qYx3/fYp6//Lx8f/W1dJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNi4pA+Pj4/9HQ0P+NioH/U1tP/zxHN/8uPCj/ESwE/xc7C/8jTxb/L2Yo/zt5Nv9EiET/SJNM/0qXTv9BhTL/LF8A/0xgQv/s6+v/3dzbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMnHxRDn5eSv7u3rv/Ly8P/4+Pj/+Pj4/+Pi4v/Pzs7/s7Sw/5CUi/9kbl//T1tH/zhMLv8mRxz/I0wa/yhWIf8mPxn/zMzJ/+no578AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMrIxSDKyMVA5eTjgOrp6J/t7Ou/8vHv7/X18///////5ubl/9rZ2f/GxsP/lZmR/7q7t//x8e/vysjFEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAysjGEMrIxUDh4N9w6Ofmj+3s7L/w7+7f8vHx/9XT0lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AP//+AAB//AAAD/gAAA/wAAAf4AAAP8AAAA+AAAAPAAAADwAAAAGAAAAA8AAAAOAAAAHgAAABwAAAAMAAAACAAAAAAAAAAQAAAAMAAAADAAAABwAAAA/AAAAP+AAAH/gAAD/wAAB/8AAAf+AAAP/gAAH/4AAD//4AA///+Af/KAAAABAAAAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbGx8mISAjsx0dIXoYGBs8AAAADQAAAAgAAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANJycouL69vv+wsLD/enp8/zQzNuQfHyGeGxseYQAAABEAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAALJCIkVXVrQf/lzmj/7d6i//Try///////2Nja/4mJiv9SUVT/JSUpsicnKkAAAAAAAAAAAAAAAAAAAAAHAAAAETU0N8bbz53/59Bm/+PITv/fxUP/3sRI/+bSgP/w5bv/+vfp/9nZ2f8rKy+/AAAAAAAAAAAAAAABAAAABTs6OmWdlnr/+/bg///////69eP/5Nek/8y2U//ewzn/2r0s/9i6Dv9pYD7/Ly8wUwAAAAEAAAAAAAAAAEdGRhBUUEnf7NmC/6mZV/8VOj//5taU/09HPv9nQwD/TEMz/3xyUP+HekH/MjI0qwAAABQAAAAKAAAAAQAAAABNTEyAvrF+/97Mg/8AUnz/AHGl/1dROf+tZwD/95UA//ihAP/IigD/k2oA/11GHP8kIybEJCQngiQkJyBWVFQQbGhh7//86P/Ava//AHXC/wCY1v9UNQD/95IA//qkAP/mpwD/pYwA/5uDAP9cYw3/M1AA/zxuOf8rNi//W1pYn9jJjv//7p//0cOB/wBiov8Avf//OjsG//iZAP99UwD/NkcA/0KiYv8bdHH/MNL//xZ4e/8/jmH/Ly4wv3V0c/////r///vo///31/8WRmH/AL7//wC02f8XNi//AJji/ww4Lv9M6MX/ObKS/xBHOP8ue1X/OD887zQzNhBmZGOAZmNiv3p4d++LiYn/Vkg1/wBccv8A3P//AM///wC7//8MOC7/TOnF/x2Hiv8w0f//GFlo/z07PUAAAAAAAAAAAAAAAAAAAAAAUVBQr0cvAP8AbZb/ANz//wDO//8AvP//Czct/0zlwP9Dw5r/MVhE/zpBPI8AAAAAAAAAAAAAAAAAAAAAWlhYQFVJJ/8cKQD/Bh0R/wEwP/8EGQ3/BiAW/w1cYv84pH3/Sq+H/01MTM8AAAAAAAAAAAAAAAAAAAAAAAAAAFtZU98uSQD/OIVD/xxvZv800v//GHt+/x11b/810v//GnF3/1JnXe9TUlIgAAAAAAAAAAAAAAAAAAAAAAAAAABhX1nvWXBX/1N9Wf9JfFb/PnhR/zyaYP87m1//NYlP/05qVv9aWFhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZkYyBmZGJAZmRicGVjYo9lY2K/YWtg71xvXf9iYF6fAAAAAAAAAAAAAAAAAAAAAAAAAADwDwDA4AdQ1YADAQCAA6gQAAEmAIAAEBCAAAEAAABoBAAAzhAAAAAAAAEAAOADAADABwAAwAcAAMAPAADgHwQA", - "headers" : { - "Content-Type" : "image/x-icon", - "Last-Modified" : "Tue, 14 Apr 2020 07:52:23 GMT", - "Accept-Ranges" : "bytes", - "Server" : "Microsoft-IIS/10.0", - "X-ArcGIS-Instance" : "MTSDS_Web_IN_3", - "X-Powered-By" : "ASP.NET", - "ETag" : "\"ded4d2a13112d61:0\"", - "Date" : "Mon, 01 Jun 2020 16:36:23 GMT", - "X-Cache" : "RefreshHit from cloudfront", - "Via" : "1.1 4b2d2d4c49521bf403522140b78283a1.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "DEN50-C2", - "X-Amz-Cf-Id" : "is5GwycouBbCks1LdP9bOi2WtpX8rqYqkVTH_C50BbctKNMwT3xoJQ==" - } - }, - "uuid" : "b9f04630-0820-4f01-a541-b9d4226b8f84", - "persistent" : true, - "scenarioName" : "scenario-1-favicon.ico", - "requiredScenarioState" : "scenario-1-favicon.ico-2", - "newScenarioState" : "scenario-1-favicon.ico-3", - "insertionIndex" : 26 -} \ No newline at end of file diff --git a/src/test/resources/mappings/faviconico-ca7c6f18-2915-4be6-b28a-1729a6217b54.json b/src/test/resources/mappings/faviconico-ca7c6f18-2915-4be6-b28a-1729a6217b54.json deleted file mode 100644 index cf5b1921..00000000 --- a/src/test/resources/mappings/faviconico-ca7c6f18-2915-4be6-b28a-1729a6217b54.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "id" : "ca7c6f18-2915-4be6-b28a-1729a6217b54", - "name" : "faviconico", - "request" : { - "url" : "/favicon.ico", - "method" : "GET" - }, - "response" : { - "status" : 200, - "base64Body" : "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAHh0lEQVRYR5WXe1BU9xXHP/fu3WVZdrkuiKDsGsAgKgri21HxQawx0bWNTdJHGpu009E0zSSNjW3HdjLNNCY1mXEmSWM7GadOnE6qGaOI1mittQXF+iCigoDvBXFZFvbuC9hn/3Ahu3dXk37/ufd3zvmd7/md373nd34CXwOy1SYAc4FVQAFQD+xS7LXhuF4CngEWAncrp03458d/+uXJh6z5g1/lW3iQMk78beA3wDSV+gCwJv6+H1idqLRaxlypmFL8/hubn/toQvG4Ie6D+wYgW21WYKc+U7c0Eo4QCkXSma2KP+vSKQFWrZj3xeyZkza8vOGJxnR6TTqhbLUtzNDrTlSUF09ZWTMHh7MfxeMHwGjMBCASiZKbm321uKigRNJKC32+AcxmE7KcRSBwb8HFRQW0XbUXhEORp5/70QbH0cOfNqm5UjIgW23LJK2mbvWKeZn5Y3IAcPT0cfJMC3NnTEIURc43dzA6V2bOjEm7ykotuQcPn14pZ2dhNpsQRQGXy8O+vzcQCUchHnRNdVWwqrJ04ysvrH0vkU9UkU8G9pllY2Zrh31Enj8mh5pFVRizMrnd1cPoXJkc2URBvjnrL389ciUSjTBqlBFJ0iCKInl5o5g6uTjRNSajQed0urd+8NH+pG9lJADZatMDewGTs1fhStttwuEv9z07OwuAcfm5eH0DKL4AWYZM3ZKFlQX/qm/mk8+OE41GR+ynTS5Gp5MA8PkGuN3pwOlSMrq6ev98oqG5MCUA4HfApOGBRhKJRmMkIjs7C0thHssWTWfKxPEcPX6uJRgKeWZNL8XvG6QtIWuCADqdluxsAwC3OnvQaSXciq+gvvHih7FYTBgJQLbaSoCXE8ks4/JGVpCIoaEgl1tvsreuPub3D+x7tGbO9YcnWGJGYyaOXjcAXm+A2sOnyMjQUjbBgiAIOJz9dN5xonj8eH2Bxz7ccWApCRnYBGiTiIKhFHIAn3+Q+sZLMa83sPHgnjcHFy+oaGu6ePV1n28g1nGtk75+L46efhTFj8vl4UxTO7FYjMGBIKfOtCBJGmJRND3O/l8DCLLVZgR6gMxhEp1OYq2tGjm+74kwmQydMypK31u5fPYHwCygEvCtW/921/5DJ7doJLFKkiSGBoMpcwGe/c5yDAY9oiCEJpWNL5EAWyI5QPnkohRyURBCFVNL6tauqf63VtJEHR/3fNPToDytH6/vLdxY2Lxz+6bBxjMtNY+u/dW6SDi4BdCryU0mAwaDHo/Hjz8wqL1w6doaCahRG6qrniASfXzF3O0L5k69OnQnqG99qf0x5aiyiPC9LXTsdHTmfTdv67zXpxQp9tptstXWBBxTFzqvN4C9s4fmlht4fQHcbt8MKZ7GJHTecSaNR+fIVxbMnXo1GoyKlx65+FLoVqgwQU24M2zp3tq9LdAaeDEWizULgnBCttqOACsT7SaWWsnNyWZgYAhRFFi6qLJaApIrBuB2+7jr6KMg/14lNBkNToC+2r5CNXkCBKVOqRYE4Y/xcbs6AFefwrUbd1i8oALzKBPRaMwiAia1J0mrQSMmFUkAwu5IRoowGSn7ngiXy0NGhpa80aOQJA06naRPZQHCoQifHarHrfgAuH6z2wP8Y6hrsCOd/f+D4fMFIBaLIQL+dIbRSAxXnweAs+fbbgmC0NL9h+7dQNpzOY62hHefWllcVJD0d4WC4SERuKU2HEbj2VYCgUGWL5tpj8ViRfP88yTgh4Anjfkh4PcJ49Nqg8ryCUnjgaGgUwSa1YbD8HoD7N5/Yu+rLz7533jzoZ0fmL8LKAW2Ay6gCVg1PzD/8fmB+YmB1QGfJvoLhpKrq8830CbIVtvKePRq9AC/BfqBZ0wmQ84rL6ztyjLof7H++dW309gjW23ZwPNAOdAB7ACeAN4Axjz1rSXkmE30uhTcio9MfcY6IT7xp8BbgDGe3vfjK3gLqB4mMJtNlJc95BYE4Z1Pdmz+T1aW3g201zdeDK96avP3gK3AmISY+oHXgN3AprFjc382s6LUdPpcq99SmPfm8QPvbhnpiGSrLQMYC9yNH80/v1/L9siSGUwrL+4db80/pxHFyLmm9qkHPj81XvH4R7ogFRqA78d9jwW63bf3BwVBiKVryV4F3knnBcBSmMc3ls5Ke1S7+jzs2Xci7TzgOjBFsdcmdcjp6sCzaWQjqCwvSUsOkKHTMrtqIiXFYzGZDGp1CbBCLUznKelkVKOl/RZWS+I2fwmjMZOZVWUARKNRdu05RsCfdDeR1XPSZeBgGtkIbty8y6kzLUQiafd6BA2nL6vJI0DK3SDlI9PLZQ3AMsCi1g3D0dNPJBrFWpiXVn/2i3YuXLymFr+m2GtTLjApGVDstb54j/A3tS4Rcuoec+HydfbW1XP2fGJFxg/8QLHXvpsy4WvcDdfF/+2kpZrNJp5cU40oikQiURSPn6bmDjqudaldHAfWK/badrViGA8MgHtBGIGfAD8GJgNMr3iYWdMn0txyHXuXk+5uV+KUKHAE2KbYaz+/r+M4vjKARMhWWymwuLioYI5Opy1sa7dnxQl74ofaOeCYYq9NiuhB+B9nAMhoxt6i3QAAAABJRU5ErkJggg==", - "headers" : { - "Content-Type" : "image/x-icon", - "Date" : "Wed, 20 May 2020 20:48:55 GMT", - "Last-Modified" : "Tue, 28 May 2019 19:23:26 GMT", - "ETag" : "\"753bb356c99d2af802399138b68e11a2\"", - "Accept-Ranges" : "bytes", - "Server" : "AmazonS3", - "X-Cache" : "Miss from cloudfront", - "Via" : "1.1 f33529eebdae9f360e1e83d3ee6348f6.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "SFO5-C3", - "X-Amz-Cf-Id" : "hgNIOJVi2BMfl0pDZwuWYzTO_VRcX1M96LFOz2_72BRIkJd38CB0_Q==" - } - }, - "uuid" : "ca7c6f18-2915-4be6-b28a-1729a6217b54", - "persistent" : true, - "insertionIndex" : 17 -} \ No newline at end of file diff --git a/src/test/resources/mappings/josm_mapwithai_json_conflation_serversjson-fb2197f1-81fd-445d-bb90-b6219d187af6.json b/src/test/resources/mappings/josm_mapwithai_json_conflation_serversjson-fb2197f1-81fd-445d-bb90-b6219d187af6.json deleted file mode 100644 index e92643a8..00000000 --- a/src/test/resources/mappings/josm_mapwithai_json_conflation_serversjson-fb2197f1-81fd-445d-bb90-b6219d187af6.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "id" : "fb2197f1-81fd-445d-bb90-b6219d187af6", - "name" : "josm_mapwithai_json_conflation_serversjson", - "request" : { - "url" : "/MapWithAI/json/conflation_servers.json", - "method" : "GET" - }, - "response" : { - "status" : 200, - "jsonBody": { - "Taylor's Address Conflation Server": { - "categories": [ - "addresses" - ], - "description": "Originally developed for use with local datasets, it now accepts external datasets for conflation purposes. In the event of a failure, the plugin will use the original dataset.", - "license": "AGPL", - "source": "{{ request.baseUrl }}/smocktaylor/serve_osm_files/", - "url": "{{ request.baseUrl }}/conflate" - } - }, - "transformers": [ - "response-template" - ], - "headers" : { - "Accept-Ranges" : "bytes", - "Cache-Control" : "max-age=600", - "Content-Type" : "application/json", - "Expires" : "Wed, 24 Jun 2020 21:26:07 UTC", - "Last-Modified" : "Wed, 24 Jun 2020 17:33:46 GMT", - "Vary" : "Origin", - "Date" : "Wed, 24 Jun 2020 21:16:07 GMT" - } - }, - "uuid" : "fb2197f1-81fd-445d-bb90-b6219d187af6", - "persistent" : true, - "insertionIndex" : 43 -} diff --git a/src/test/resources/mappings/josm_mapwithai_json_sourcesjson-23497750-675c-440a-986a-34dd14b047f8.json b/src/test/resources/mappings/josm_mapwithai_json_sourcesjson-23497750-675c-440a-986a-34dd14b047f8.json deleted file mode 100644 index 87915c09..00000000 --- a/src/test/resources/mappings/josm_mapwithai_json_sourcesjson-23497750-675c-440a-986a-34dd14b047f8.json +++ /dev/null @@ -1,641 +0,0 @@ -{ - "id" : "23497750-675c-440a-986a-34dd14b047f8", - "name" : "josm_mapwithai_json_sourcesjson", - "request" : { - "urlPattern" : "\/MapWithAI\/?json\/sources.json", - "method" : "GET" - }, - "response" : { - "status" : 200, - "jsonBody": { - "Statewide Aggregate Addresses in Colorado 2019 (Public)": { - "countries": { - "US-CO": [ - "addr:housenumber" - ] - }, - "license": "Public Domain", - "osm_compatible": "yes", - "parameters": [], - "permission_url": "{{ request.baseUrl }}/wiki/Import/Colorado_Addresses", - "url": "{{ request.baseUrl }}/coloradoAddresses/map?bbox={bbox}" - }, - "MapWithAI": { - "countries": { - "AE": [ - "highway" - ], - "AF": [ - "highway" - ], - "AG": [ - "highway" - ], - "AI": [ - "highway" - ], - "AL": [ - "highway" - ], - "AM": [ - "highway" - ], - "AO": [ - "highway" - ], - "AR": [ - "highway" - ], - "AT": [ - "highway" - ], - "AU": [ - "highway" - ], - "AZ": [ - "highway" - ], - "BA": [ - "highway" - ], - "BB": [ - "highway" - ], - "BD": [ - "highway" - ], - "BE": [ - "highway" - ], - "BF": [ - "highway" - ], - "BG": [ - "highway" - ], - "BI": [ - "highway" - ], - "BJ": [ - "highway" - ], - "BL": [ - "highway" - ], - "BN": [ - "highway" - ], - "BO": [ - "highway" - ], - "BR": [ - "highway" - ], - "BS": [ - "highway" - ], - "BT": [ - "highway" - ], - "BW": [ - "highway" - ], - "BY": [ - "highway" - ], - "BZ": [ - "highway" - ], - "CA": [ - "building", - "highway" - ], - "CD": [ - "highway" - ], - "CF": [ - "highway" - ], - "CG": [ - "highway" - ], - "CH": [ - "highway" - ], - "CI": [ - "highway" - ], - "CL": [ - "highway" - ], - "CM": [ - "highway" - ], - "CN": [ - "highway" - ], - "CO": [ - "highway" - ], - "CR": [ - "highway" - ], - "CU": [ - "highway" - ], - "CY": [ - "highway" - ], - "CZ": [ - "highway" - ], - "DE": [ - "highway" - ], - "DJ": [ - "highway" - ], - "DK": [ - "highway" - ], - "DM": [ - "highway" - ], - "DO": [ - "highway" - ], - "DZ": [ - "highway" - ], - "EC": [ - "highway" - ], - "EE": [ - "highway" - ], - "EG": [ - "highway" - ], - "EH": [ - "highway" - ], - "ER": [ - "highway" - ], - "ES": [ - "highway" - ], - "ET": [ - "highway" - ], - "FK": [ - "highway" - ], - "FI": [ - "highway" - ], - "FJ": [ - "highway" - ], - "FR": [ - "highway" - ], - "GA": [ - "highway" - ], - "GB": [ - "highway" - ], - "GD": [ - "highway" - ], - "GE": [ - "highway" - ], - "GF": [ - "highway" - ], - "GH": [ - "highway" - ], - "GM": [ - "highway" - ], - "GN": [ - "highway" - ], - "GP": [ - "highway" - ], - "GQ": [ - "highway" - ], - "GR": [ - "highway" - ], - "GT": [ - "highway" - ], - "GW": [ - "highway" - ], - "GY": [ - "highway" - ], - "HN": [ - "highway" - ], - "HR": [ - "highway" - ], - "HT": [ - "highway" - ], - "HU": [ - "highway" - ], - "ID": [ - "highway" - ], - "IE": [ - "highway" - ], - "IL": [ - "highway" - ], - "IN": [ - "highway" - ], - "IQ": [ - "highway" - ], - "IS": [ - "highway" - ], - "IT": [ - "highway" - ], - "JM": [ - "highway" - ], - "JO": [ - "highway" - ], - "JP": [ - "highway" - ], - "KE": [ - "highway" - ], - "KG": [ - "highway" - ], - "KH": [ - "highway" - ], - "KN": [ - "highway" - ], - "KY": [ - "highway" - ], - "KR": [ - "highway" - ], - "KW": [ - "highway" - ], - "KZ": [ - "highway" - ], - "LA": [ - "highway" - ], - "LB": [ - "highway" - ], - "LC": [ - "highway" - ], - "LK": [ - "highway" - ], - "LR": [ - "highway" - ], - "LS": [ - "highway" - ], - "LT": [ - "highway" - ], - "LU": [ - "highway" - ], - "LV": [ - "highway" - ], - "LY": [ - "highway" - ], - "MA": [ - "highway" - ], - "MD": [ - "highway" - ], - "ME": [ - "highway" - ], - "MF": [ - "highway" - ], - "MG": [ - "highway" - ], - "MK": [ - "highway" - ], - "ML": [ - "highway" - ], - "MM": [ - "highway" - ], - "MN": [ - "highway" - ], - "MQ": [ - "highway" - ], - "MR": [ - "highway" - ], - "MS": [ - "highway" - ], - "MW": [ - "highway" - ], - "MX": [ - "highway" - ], - "MY": [ - "highway" - ], - "MZ": [ - "highway" - ], - "NA": [ - "highway" - ], - "NE": [ - "highway" - ], - "NG": [ - "highway" - ], - "NI": [ - "highway" - ], - "NL": [ - "highway" - ], - "NL-BQ2": [ - "highway" - ], - "NL-BQ3": [ - "highway" - ], - "NO": [ - "highway" - ], - "NP": [ - "highway" - ], - "NZ": [ - "highway" - ], - "OM": [ - "highway" - ], - "PA": [ - "highway" - ], - "PE": [ - "highway" - ], - "PF": [ - "highway" - ], - "PG": [ - "highway" - ], - "PH": [ - "highway" - ], - "PK": [ - "highway" - ], - "PL": [ - "highway" - ], - "PR": [ - "highway" - ], - "PS": [ - "highway" - ], - "PT": [ - "highway" - ], - "PY": [ - "highway" - ], - "QA": [ - "highway" - ], - "RO": [ - "highway" - ], - "RS": [ - "highway" - ], - "RS-KM": [ - "highway" - ], - "RU": [ - "highway" - ], - "RW": [ - "highway" - ], - "SA": [ - "highway" - ], - "SB": [ - "highway" - ], - "SD": [ - "highway" - ], - "SE": [ - "highway" - ], - "SG": [ - "highway" - ], - "SI": [ - "highway" - ], - "SK": [ - "highway" - ], - "SL": [ - "highway" - ], - "SN": [ - "highway" - ], - "SO": [ - "highway" - ], - "SR": [ - "highway" - ], - "SS": [ - "highway" - ], - "ST": [ - "highway" - ], - "SV": [ - "highway" - ], - "SX": [ - "highway" - ], - "SZ": [ - "highway" - ], - "TC": [ - "highway" - ], - "TD": [ - "highway" - ], - "TG": [ - "highway" - ], - "TH": [ - "highway" - ], - "TJ": [ - "highway" - ], - "TL": [ - "highway" - ], - "TM": [ - "highway" - ], - "TN": [ - "highway" - ], - "TR": [ - "highway" - ], - "TT": [ - "highway" - ], - "TW": [ - "highway" - ], - "TZ": [ - "building", - "highway" - ], - "UA": [ - "highway" - ], - "UG": [ - "building", - "highway" - ], - "US": [ - "building", - "highway" - ], - "US-AK": [ - "building" - ], - "US-HI": [ - "building" - ], - "UY": [ - "highway" - ], - "UZ": [ - "highway" - ], - "VC": [ - "highway" - ], - "VE": [ - "highway" - ], - "VG": [ - "highway" - ], - "VN": [ - "highway" - ], - "VU": [ - "highway" - ], - "YE": [ - "highway" - ], - "ZA": [ - "highway" - ], - "ZM": [ - "highway" - ], - "ZW": [ - "highway" - ] - }, - "default": true, - "license": "ODBL", - "osm_compatible": "yes", - "parameters": [ - { - "description": "buildings", - "enabled": true, - "parameter": "result_type=road_building_vector_xml" - } - ], - "permission_url": "{{ request.baseUrl }}/facebookmicrosites/Open-Mapping-At-Facebook/wiki/FAQ", - "terms_of_use_url": "{{ request.baseUrl }}/doc/license/MapWithAILicense.pdf", - "privacy_policy_url": "{{ request.baseUrl }}/doc/license/MapWithAIPrivacyPolicy.pdf#page=3", - "url": "{{ request.baseUrl }}/maps/ml_roads?conflate_with_osm=true&theme=ml_road_vector&collaborator=josm&token=ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m&hash=ASawRla3rBcwEjY4HIY&bbox={bbox}" - } - }, - "transformers": [ - "response-template" - ], - "headers" : { - "Accept-Ranges" : "bytes", - "Cache-Control" : "max-age=600", - "Content-Type" : "application/json", - "Expires" : "Tue, 21 Apr 2020 19:51:40 UTC", - "Last-Modified" : "Tue, 21 Apr 2020 17:34:08 GMT", - "Vary" : "Origin", - "Date" : "Tue, 21 Apr 2020 19:41:40 GMT" - } - }, - "uuid" : "23497750-675c-440a-986a-34dd14b047f8", - "persistent" : true, - "insertionIndex" : 1 -} diff --git a/src/test/resources/mappings/josmfile-Styles-MapWithAI.json b/src/test/resources/mappings/josmfile-Styles-MapWithAI.json deleted file mode 100644 index c3f8700c..00000000 --- a/src/test/resources/mappings/josmfile-Styles-MapWithAI.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "GET", - "url": "/josmfile?page=Styles/MapWithAI&zip=1" - }, - "response": { - "status": 200, - "bodyFileName": "josmfile?page=Styles/MapWithAI&zip=1", - "headers": { - "Content-Type": "application/zip", - "Content-Disposition": "attachment; filename=Styles_MapWithAI.zip", - "Content-Length": "8103" - } - } -} diff --git a/src/test/resources/mappings/maps_ml_roads-04b70b03-d148-4e3d-a1fc-98334cd10e64.json b/src/test/resources/mappings/maps_ml_roads-04b70b03-d148-4e3d-a1fc-98334cd10e64.json deleted file mode 100644 index f34f7d60..00000000 --- a/src/test/resources/mappings/maps_ml_roads-04b70b03-d148-4e3d-a1fc-98334cd10e64.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "id" : "04b70b03-d148-4e3d-a1fc-98334cd10e64", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-108.4495519,39.095376,-108.4422314,39.0987811"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "Strict-Transport-Security" : "max-age=15552000; preload", - "Vary" : "Accept-Encoding", - "X-Content-Type-Options" : "nosniff", - "X-Frame-Options" : "DENY", - "X-XSS-Protection" : "0", - "Access-Control-Allow-Origin" : "https://mapwith.ai", - "X-FB-Debug" : "7g3PXnF8/LGeSAoO/IVLk6j+58f9Nc65opZ9zOB6X5EcETKOxoNZNHWvVnAX+NOSLrAEniyckQSQEjy++0JEPA==", - "Date" : "Thu, 12 Dec 2019 19:52:23 GMT", - "Alt-Svc" : "h3-24=\":443\"; ma=3600", - "Connection" : "keep-alive" - } - }, - "uuid" : "04b70b03-d148-4e3d-a1fc-98334cd10e64", - "persistent" : true, - "insertionIndex" : 16 -} diff --git a/src/test/resources/mappings/maps_ml_roads-0bc0c6c3-8e38-4da4-8f85-c07bde3ef54d.json b/src/test/resources/mappings/maps_ml_roads-0bc0c6c3-8e38-4da4-8f85-c07bde3ef54d.json deleted file mode 100644 index 0399cd6d..00000000 --- a/src/test/resources/mappings/maps_ml_roads-0bc0c6c3-8e38-4da4-8f85-c07bde3ef54d.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "id" : "0bc0c6c3-8e38-4da4-8f85-c07bde3ef54d", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-5.7043009,34.4853974,-5.6686014,34.5183563"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "X-XSS-Protection" : "0", - "X-Frame-Options" : "DENY", - "Strict-Transport-Security" : "max-age=15552000; preload", - "X-Content-Type-Options" : "nosniff", - "X-FB-Debug" : "SZIzesK8Y75ra73sB6AEb1fu4HFilrkmT2RMoT64I1I/V98YIs79PkACPGCJfiJhK7SYsf8gxXjpD9t58nroNQ==", - "Date" : "Wed, 20 May 2020 20:52:00 GMT", - "Alt-Svc" : "h3-27=\":443\"; ma=3600", - "Vary" : "Accept-Encoding", - "X-Cache" : "Miss from cloudfront", - "Via" : "1.1 afb9be97319013ab1a18f338fce40f2a.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "SFO5-C1", - "X-Amz-Cf-Id" : "pKrKXnai7b7HG__Dr1iNDBVr4ed5OfcmtZt13CaRES9JC2TGJLiVjQ==" - } - }, - "uuid" : "0bc0c6c3-8e38-4da4-8f85-c07bde3ef54d", - "persistent" : true, - "insertionIndex" : 18 -} diff --git a/src/test/resources/mappings/maps_ml_roads-146193dd-3082-495b-b350-8164e2858f8d.json b/src/test/resources/mappings/maps_ml_roads-146193dd-3082-495b-b350-8164e2858f8d.json deleted file mode 100644 index c45c2bb1..00000000 --- a/src/test/resources/mappings/maps_ml_roads-146193dd-3082-495b-b350-8164e2858f8d.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "id" : "146193dd-3082-495b-b350-8164e2858f8d", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-0.001,-0.001,0.0,0.0"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "Strict-Transport-Security" : "max-age=15552000; preload", - "Vary" : "Accept-Encoding", - "X-Content-Type-Options" : "nosniff", - "X-Frame-Options" : "DENY", - "X-XSS-Protection" : "0", - "Access-Control-Allow-Origin" : "https://mapwith.ai", - "X-FB-Debug" : "qE8O2lKCwh9FH8Ls+TyRrL0PM87qbcaZpFqnNwRft2Iby5lKiXh0YyNGocjYZR9bzNa+DdVLLvdsHqc/jEzI5g==", - "Date" : "Thu, 05 Dec 2019 14:38:35 GMT", - "Alt-Svc" : "h3-23=\":443\"; ma=3600", - "Connection" : "keep-alive" - } - }, - "uuid" : "146193dd-3082-495b-b350-8164e2858f8d", - "persistent" : true, - "insertionIndex" : 4 -} diff --git a/src/test/resources/mappings/maps_ml_roads-3098faf3-c028-43c6-9263-42f1cab7c161.json b/src/test/resources/mappings/maps_ml_roads-3098faf3-c028-43c6-9263-42f1cab7c161.json deleted file mode 100644 index 08f1703d..00000000 --- a/src/test/resources/mappings/maps_ml_roads-3098faf3-c028-43c6-9263-42f1cab7c161.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "id" : "3098faf3-c028-43c6-9263-42f1cab7c161", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "0.0,0.0,0.001,0.001"}, - "crop_bbox": { "equalTo": "0.0,0.0,0.001,0.001"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "X-XSS-Protection" : "0", - "X-Frame-Options" : "DENY", - "Strict-Transport-Security" : "max-age=15552000; preload", - "X-Content-Type-Options" : "nosniff", - "X-FB-Debug" : "Q5q6qaT8u3upIyZAE6m4UiHMBr8fF67nlnhkwc5kzXlXz93UpmkRTuXJx43bdPz5nFZA92SntpW1hek9gyGRvg==", - "Date" : "Wed, 20 May 2020 20:44:39 GMT", - "Alt-Svc" : "h3-27=\":443\"; ma=3600", - "Vary" : "Accept-Encoding", - "X-Cache" : "Miss from cloudfront", - "Via" : "1.1 e9ba0a9a729ff2960a04323bf1833df8.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "SFO5-C3", - "X-Amz-Cf-Id" : "X4BTMHf9EGCyN0d6zqSM5U-sqa3Ypk1nICQFdebHJD8qaYdMAvQ8xA==" - } - }, - "uuid" : "3098faf3-c028-43c6-9263-42f1cab7c161", - "persistent" : true, - "insertionIndex" : 15 -} diff --git a/src/test/resources/mappings/maps_ml_roads-4df1e72e-a62e-48b6-806d-99fd74956742.json b/src/test/resources/mappings/maps_ml_roads-4df1e72e-a62e-48b6-806d-99fd74956742.json deleted file mode 100644 index 7562fe17..00000000 --- a/src/test/resources/mappings/maps_ml_roads-4df1e72e-a62e-48b6-806d-99fd74956742.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "id" : "4df1e72e-a62e-48b6-806d-99fd74956742", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-108.4625,39.0621,-108.4594,39.0633"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "Strict-Transport-Security" : "max-age=15552000; preload", - "Vary" : "Accept-Encoding", - "X-Content-Type-Options" : "nosniff", - "X-Frame-Options" : "DENY", - "X-XSS-Protection" : "0", - "Access-Control-Allow-Origin" : "https://mapwith.ai", - "X-FB-Debug" : "QOH/uVezGcDgTnRr2jZ1IkfdKKqM58LWaV/SlCdAL7YdKiWbBLck26+xgQpnTcAAWJ06oVbVNddoCu+fZ0F4Tg==", - "Date" : "Thu, 05 Dec 2019 14:46:51 GMT", - "Alt-Svc" : "h3-23=\":443\"; ma=3600", - "Connection" : "keep-alive" - } - }, - "uuid" : "4df1e72e-a62e-48b6-806d-99fd74956742", - "persistent" : true, - "insertionIndex" : 7 -} diff --git a/src/test/resources/mappings/maps_ml_roads-53133862-d606-4976-b016-a35be71df3fb.json b/src/test/resources/mappings/maps_ml_roads-53133862-d606-4976-b016-a35be71df3fb.json deleted file mode 100644 index 1c70b7b9..00000000 --- a/src/test/resources/mappings/maps_ml_roads-53133862-d606-4976-b016-a35be71df3fb.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "id" : "53133862-d606-4976-b016-a35be71df3fb", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-5.7400005,34.4853974,-5.7043009,34.5183563"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "Strict-Transport-Security" : "max-age=15552000; preload", - "Vary" : "Accept-Encoding", - "X-Content-Type-Options" : "nosniff", - "X-Frame-Options" : "DENY", - "X-XSS-Protection" : "0", - "Access-Control-Allow-Origin" : "https://mapwith.ai", - "X-FB-Debug" : "rZgAVHsgMidbc1qG6uZ1HnY8AZong/MMSmxfqF5F2dIS13VTzs9pj0jHwSGPEXbrUdvC1JetW7YaojRdoZAtyg==", - "Date" : "Thu, 09 Jan 2020 22:32:05 GMT", - "Alt-Svc" : "h3-24=\":443\"; ma=3600", - "Connection" : "keep-alive" - } - }, - "uuid" : "53133862-d606-4976-b016-a35be71df3fb", - "persistent" : true, - "insertionIndex" : 24 -} diff --git a/src/test/resources/mappings/maps_ml_roads-580f7b5b-cb9a-4092-8a39-bf8b714e6efe.json b/src/test/resources/mappings/maps_ml_roads-580f7b5b-cb9a-4092-8a39-bf8b714e6efe.json deleted file mode 100644 index bf54fb5a..00000000 --- a/src/test/resources/mappings/maps_ml_roads-580f7b5b-cb9a-4092-8a39-bf8b714e6efe.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "id" : "580f7b5b-cb9a-4092-8a39-bf8b714e6efe", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-108.5715723,39.0734162,-108.5707107,39.0738791"}, - "crop_bbox": { "equalTo": "-108.5711561,39.0735205,-108.5708568,39.0736682"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "Strict-Transport-Security" : "max-age=15552000; preload", - "Vary" : "Accept-Encoding", - "X-Content-Type-Options" : "nosniff", - "X-Frame-Options" : "DENY", - "X-XSS-Protection" : "0", - "Access-Control-Allow-Origin" : "https://mapwith.ai", - "X-FB-Debug" : "KVSWyv0yjs/bb9oG3tIz+ORdsb/MOp21ZqyqZ4PYVMtcBatm9nD9St1AD+4Y940a7kRKNpcGGjRzWAFf7D8Z2A==", - "Date" : "Tue, 17 Dec 2019 21:56:59 GMT", - "Alt-Svc" : "h3-24=\":443\"; ma=3600", - "Connection" : "keep-alive" - } - }, - "uuid" : "580f7b5b-cb9a-4092-8a39-bf8b714e6efe", - "persistent" : true, - "insertionIndex" : 17 -} diff --git a/src/test/resources/mappings/maps_ml_roads-715854d4-ba75-4289-89fe-9352c3d3ed6b.json b/src/test/resources/mappings/maps_ml_roads-715854d4-ba75-4289-89fe-9352c3d3ed6b.json deleted file mode 100644 index 88688c46..00000000 --- a/src/test/resources/mappings/maps_ml_roads-715854d4-ba75-4289-89fe-9352c3d3ed6b.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "id" : "715854d4-ba75-4289-89fe-9352c3d3ed6b", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-108.5715723,39.0734162,-108.5707107,39.0738791"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "Strict-Transport-Security" : "max-age=15552000; preload", - "Vary" : "Accept-Encoding", - "X-Content-Type-Options" : "nosniff", - "X-Frame-Options" : "DENY", - "X-XSS-Protection" : "0", - "Access-Control-Allow-Origin" : "https://mapwith.ai", - "X-FB-Debug" : "uhd2nZW/X8sd5JhczSOjcv1ho6t3ZigTidNma/6Ufz5LENsI8a7coxvxs5h5lhF2507g9cfL1cQNL1JSP5Ugkg==", - "Date" : "Thu, 05 Dec 2019 14:25:07 GMT", - "Alt-Svc" : "h3-23=\":443\"; ma=3600", - "Connection" : "keep-alive" - } - }, - "uuid" : "715854d4-ba75-4289-89fe-9352c3d3ed6b", - "persistent" : true, - "insertionIndex" : 2 -} diff --git a/src/test/resources/mappings/maps_ml_roads-9b87a83b-8600-4f49-9362-9bb066b02133.json b/src/test/resources/mappings/maps_ml_roads-9b87a83b-8600-4f49-9362-9bb066b02133.json deleted file mode 100644 index 2da33d71..00000000 --- a/src/test/resources/mappings/maps_ml_roads-9b87a83b-8600-4f49-9362-9bb066b02133.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "id" : "9b87a83b-8600-4f49-9362-9bb066b02133", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-5.7043009,34.5183563,-5.6686014,34.5513153"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "X-XSS-Protection" : "0", - "X-Frame-Options" : "DENY", - "Strict-Transport-Security" : "max-age=15552000; preload", - "X-Content-Type-Options" : "nosniff", - "X-FB-Debug" : "OROCzsIr5oRJ6A5QUfRqMNY1O0091VmJj6KkYKAFd7q072iY9hPLtshWHrVgMNHbFozfg0JR8nJKtSmAMeqjbw==", - "Date" : "Wed, 20 May 2020 20:55:44 GMT", - "Alt-Svc" : "h3-27=\":443\"; ma=3600", - "Vary" : "Accept-Encoding", - "X-Cache" : "Miss from cloudfront", - "Via" : "1.1 4d573cd6a6c0581b92beae04909c3dce.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "SFO5-C1", - "X-Amz-Cf-Id" : "sEtObjYFlGs7EMVyLW_kdeA6MM__wD4kXPSlLmxRQQuL99ZAloAypA==" - } - }, - "uuid" : "9b87a83b-8600-4f49-9362-9bb066b02133", - "persistent" : true, - "insertionIndex" : 19 -} diff --git a/src/test/resources/mappings/maps_ml_roads-ce324a60-116f-48ae-8999-fa4321b68f18.json b/src/test/resources/mappings/maps_ml_roads-ce324a60-116f-48ae-8999-fa4321b68f18.json deleted file mode 100644 index 2dc684fa..00000000 --- a/src/test/resources/mappings/maps_ml_roads-ce324a60-116f-48ae-8999-fa4321b68f18.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "id" : "ce324a60-116f-48ae-8999-fa4321b68f18", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-5.7043009,34.4524384,-5.6686014,34.4853974"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "X-XSS-Protection" : "0", - "X-Frame-Options" : "DENY", - "Strict-Transport-Security" : "max-age=15552000; preload", - "X-Content-Type-Options" : "nosniff", - "X-FB-Debug" : "tShtKd3yucM2HkxiIVTOmzj5APY8ml2c8p8SYW238duO4OnIgfjDAvS+zmjTTXKNJOLWvl5TnTNYgKlbykO0dQ==", - "Date" : "Wed, 20 May 2020 20:59:24 GMT", - "Alt-Svc" : "h3-27=\":443\"; ma=3600", - "Vary" : "Accept-Encoding", - "X-Cache" : "Miss from cloudfront", - "Via" : "1.1 afb9be97319013ab1a18f338fce40f2a.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "SFO5-C1", - "X-Amz-Cf-Id" : "nbw03FAK0NVsx91SW52NB2EWNVR2Ebeq58nKwjVqhVzaqlbGmbb0lA==" - } - }, - "uuid" : "ce324a60-116f-48ae-8999-fa4321b68f18", - "persistent" : true, - "insertionIndex" : 21 -} diff --git a/src/test/resources/mappings/maps_ml_roads-e3da5e5b-126f-44e1-8411-93eff12bd08a.json b/src/test/resources/mappings/maps_ml_roads-e3da5e5b-126f-44e1-8411-93eff12bd08a.json deleted file mode 100644 index 7da6189c..00000000 --- a/src/test/resources/mappings/maps_ml_roads-e3da5e5b-126f-44e1-8411-93eff12bd08a.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "id" : "e3da5e5b-126f-44e1-8411-93eff12bd08a", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-5.7400005,34.4524384,-5.7043009,34.4853974"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "X-XSS-Protection" : "0", - "X-Frame-Options" : "DENY", - "Strict-Transport-Security" : "max-age=15552000; preload", - "X-Content-Type-Options" : "nosniff", - "X-FB-Debug" : "pJqpCkKG1dgh3Y5FBG1ewTieNinpbIXXCy/y4dqkSFKtySkTV52GgYkeXYxkmKK7ChcK6JtvH+TBM+G6Sj7jng==", - "Date" : "Wed, 20 May 2020 20:48:54 GMT", - "Alt-Svc" : "h3-27=\":443\"; ma=3600", - "Vary" : "Accept-Encoding", - "X-Cache" : "Miss from cloudfront", - "Via" : "1.1 f33529eebdae9f360e1e83d3ee6348f6.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "SFO5-C3", - "X-Amz-Cf-Id" : "iRT1cOtxDTjsmQ-1nGsFJYH8lNNBpTdzG5PSbgMjrrjElU0umAHn2w==" - } - }, - "uuid" : "e3da5e5b-126f-44e1-8411-93eff12bd08a", - "persistent" : true, - "insertionIndex" : 16 -} diff --git a/src/test/resources/mappings/maps_ml_roads-e543fb8b-a680-4a1d-9f90-a793b7a7a6a6.json b/src/test/resources/mappings/maps_ml_roads-e543fb8b-a680-4a1d-9f90-a793b7a7a6a6.json deleted file mode 100644 index 0a25734d..00000000 --- a/src/test/resources/mappings/maps_ml_roads-e543fb8b-a680-4a1d-9f90-a793b7a7a6a6.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "id" : "e543fb8b-a680-4a1d-9f90-a793b7a7a6a6", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "0.0,0.0,0.001,0.001"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "Strict-Transport-Security" : "max-age=15552000; preload", - "Vary" : "Accept-Encoding", - "X-Content-Type-Options" : "nosniff", - "X-Frame-Options" : "DENY", - "X-XSS-Protection" : "0", - "Access-Control-Allow-Origin" : "https://mapwith.ai", - "X-FB-Debug" : "FJjx+i5MkmXItFFBe8db1G1WdLXvQ6NIqBtGlXPCtsb0yb/h73gwbbNVdxakIqzOJbPDmFXbXJF3gBWact65eQ==", - "Date" : "Thu, 05 Dec 2019 14:36:42 GMT", - "Alt-Svc" : "h3-23=\":443\"; ma=3600", - "Connection" : "keep-alive" - } - }, - "uuid" : "e543fb8b-a680-4a1d-9f90-a793b7a7a6a6", - "persistent" : true, - "insertionIndex" : 3 -} diff --git a/src/test/resources/mappings/maps_ml_roads-f72200ba-e99d-481a-90e9-c4e7ddd3398b.json b/src/test/resources/mappings/maps_ml_roads-f72200ba-e99d-481a-90e9-c4e7ddd3398b.json deleted file mode 100644 index 0a4bc1d6..00000000 --- a/src/test/resources/mappings/maps_ml_roads-f72200ba-e99d-481a-90e9-c4e7ddd3398b.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "id" : "f72200ba-e99d-481a-90e9-c4e7ddd3398b", - "name" : "maps_ml_roads", - "request" : { - "urlPath" : "/maps/ml_roads", - "queryParameters": { - "bbox": { "equalTo": "-5.7400005,34.5183563,-5.7043009,34.5513153"}, - "result_type": { "equalTo": "road_building_vector_xml"}, - "conflate_with_osm": { "equalTo": "true"}, - "theme": { "equalTo": "ml_road_vector"}, - "collaborator": { "equalTo": "josm"}, - "token": { "equalTo": "ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m"}, - "hash": {"equalTo": "ASawRla3rBcwEjY4HIY"} - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "headers" : { - "Content-Type" : "text/xml; charset=UTF-8", - "X-XSS-Protection" : "0", - "X-Frame-Options" : "DENY", - "Strict-Transport-Security" : "max-age=15552000; preload", - "X-Content-Type-Options" : "nosniff", - "X-FB-Debug" : "mSiPFSQDfTcgE2xCH6GpcEdM0+j/A7eN/nwT34iOF9Ve/Q0Dd+2wL+Zuwp/2yYb8nhZ6ZKdZqfYfR6r3khAr8g==", - "Date" : "Wed, 20 May 2020 20:59:07 GMT", - "Alt-Svc" : "h3-27=\":443\"; ma=3600", - "Vary" : "Accept-Encoding", - "X-Cache" : "Miss from cloudfront", - "Via" : "1.1 afb9be97319013ab1a18f338fce40f2a.cloudfront.net (CloudFront)", - "X-Amz-Cf-Pop" : "SFO5-C1", - "X-Amz-Cf-Id" : "VAUNQHJcsd1hqPARg0Vt-EiH7HQYUXeQVh4wrn2ewcSSO026XV2mqA==" - } - }, - "uuid" : "f72200ba-e99d-481a-90e9-c4e7ddd3398b", - "persistent" : true, - "insertionIndex" : 20 -} diff --git a/src/test/resources/mappings/sharing_rest_content_groups_bdf6c800b3ae453b9db239e03d7c1727_search-2b3ba100-8f68-4bd8-977f-86cf5c5212b2.json b/src/test/resources/mappings/sharing_rest_content_groups_bdf6c800b3ae453b9db239e03d7c1727_search-2b3ba100-8f68-4bd8-977f-86cf5c5212b2.json deleted file mode 100644 index cdc64e10..00000000 --- a/src/test/resources/mappings/sharing_rest_content_groups_bdf6c800b3ae453b9db239e03d7c1727_search-2b3ba100-8f68-4bd8-977f-86cf5c5212b2.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "id" : "2b3ba100-8f68-4bd8-977f-86cf5c5212b2", - "name" : "sharing_rest_content_groups_bdf6c800b3ae453b9db239e03d7c1727_search", - "request" : { - "urlPath": "/sharing/rest/content/groups/bdf6c800b3ae453b9db239e03d7c1727/search", - "queryParameters": { - "f": { "equalTo": "json" }, - "sortField": { "equalTo": "added" }, - "sortOrder": { "equalTo": "desc" }, - "num": { "equalTo": "100" }, - "start": { "equalTo": "101" } - }, - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\"query\":null,\"total\":24,\"start\":101,\"num\":12,\"nextStart\":-1,\"results\":[{\"id\":\"30cef2eb73b84de79e7965e4531664ca\",\"owner\":\"dkensok_osm\",\"created\":1597788518000,\"modified\":1597788858000,\"guid\":null,\"name\":\"SanGIS_Buildings\",\"title\":\"San Diego County, CA Buildings\",\"type\":\"Feature Service\",\"typeKeywords\":[\"ArcGIS Server\",\"Data\",\"Feature Access\",\"Feature Service\",\"Service\",\"Singlelayer\",\"Hosted Service\",\"View Service\"],\"description\":\"This layer contains pre-processed building footprints with addresses for San Diego County, California that can be added to OpenStreetMap.  The data has been provided by t<\\/span><\\/font>he San Diego Geographic Information Source (SanGIS)<\\/b>, which is a Joint Powers Authority (JPA) of the City of San Diego and the County of San Diego responsible for maintaining a regional geographic information system (GIS) landbase and data warehouse.<\\/span><\\/font>

<\\/div>
Data Source<\\/b><\\/span><\\/font><\\/div>
The building footprints were downloaded in June 2020 from the <\\/font>SanGIS Regional Data Warehouse site<\\/a>.<\\/font><\\/div>

<\\/font><\\/div>
This layer contains 1,097,208 buildings for San Diego County. Of the total building features, 1,014,089 buildings<\\/b> do not intersect an existing building in OSM and<\\/span> can be added.<\\/div>

<\\/div>
Features with an Import_2_OSM value of 1 (i.e. <\\/span>Import <\\/b><\\/font>features, shown in <\\/span>Green<\\/b><\\/font>)<\\/span> have no conflict and can be added<\\/span>.  Features with an Import_2_OSM value of 0 <\\/span>(i.e. <\\/span>No Import<\\/font><\\/b> features, shown in <\\/span>Red<\\/b><\\/font>) <\\/span>conflict with existing buildings and should not be added.<\\/span><\\/div>\",\"tags\":[\"San Diego County\",\"buildings\",\"v2\",\"OpenStreetMap\"],\"snippet\":\"This layer contains pre-processed building footprints with addresses for San Diego County, California from SanGIS.\",\"thumbnail\":\"thumbnail/thumbnail1597788679289.png\",\"documentation\":null,\"extent\":[[-117.59516072599996,32.53492131200005],[-116.08113314699996,33.505188161000035]],\"categories\":[],\"spatialReference\":\"4326\",\"accessInformation\":\"SanGIS\",\"licenseInfo\":\"The data is provided by SanGIS under the terms of the <\\/span>SanGIS Legal Notice<\\/a>, with express permission to include in OpenStreetMap as provided below:<\\/span>

<\\/div>
"Esri may use selected SanGIS data downloaded from the RDW site to import into OpenStreetMap under its Open Database License per the terms and conditions on the RDW and reflects SanGIS as data owners."<\\/div>\",\"culture\":\"en-us\",\"properties\":null,\"url\":\"https://services6.arcgis.com/Do88DoK2xjTUCXd1/arcgis/rest/services/SanGIS_Buildings/FeatureServer\",\"proxyFilter\":null,\"access\":\"public\",\"size\":-1,\"subInfo\":0,\"appCategories\":[],\"industries\":[],\"languages\":[],\"largeThumbnail\":null,\"banner\":null,\"screenshots\":[],\"listed\":false,\"numComments\":0,\"numRatings\":0,\"avgRating\":0,\"numViews\":225,\"groupCategories\":[\"/Categories/Buildings\"],\"scoreCompleteness\":100,\"groupDesignations\":null,\"contentOrigin\":\"self\"},{\"id\":\"0b847f0a33884b0e89f4bf0c59fd52ca\",\"owner\":\"dkensok_osm\",\"created\":1597423764000,\"modified\":1610152362000,\"guid\":null,\"name\":\"Riverside_CA_Buildings_v2\",\"title\":\"City of Riverside, CA Buildings\",\"type\":\"Feature Service\",\"typeKeywords\":[\"ArcGIS Server\",\"Data\",\"Feature Access\",\"Feature Service\",\"Service\",\"Singlelayer\",\"Hosted Service\",\"View Service\"],\"description\":\"This layer contains pre-processed building footprints for the <\\/span>City of Riverside, California<\\/span> to be added to OpenStreetMap. <\\/span>The data has been provided by the City of Riverside and pre-processed by Esri for the explicit purpose of contributing to OpenStreetMap.<\\/span>

<\\/b><\\/span><\\/font><\\/div>
Data Source<\\/b><\\/font><\\/div>
The building footprints were originally generated by Microsoft as part of the California file downloaded from the Microsoft Buildings repo on GitHub<\\/a>. (This was the second release of building footprints. Microsoft had made a prior release of building footprints<\\/a> that did not include Riverside County.) These footprints were generated based on Bing aerials from 2014-2015. The Building Footprints from the City of Riverside generated in 2012 replaced the Microsoft footprints where they overlapped. Attributes were added by the City Transportation Department that include FIPS information, fields from the City of Riverside schema, Address IDs from the ADDRESS_POINTS feature class, Building centroid coordinates and APNs.
<\\/span><\\/font>

<\\/font><\\/div>
Update Plan<\\/b>:
<\\/span><\\/font>

<\\/div>
This layer contains 152,588 buildings for the City of Riverside. 144,438 of the building features do not currently exist in OSM (as of Apr 16) and will be imported (Import_2_OSM value of 1).  The remaining 8,150 conflict with existing buildings in OSM and will not be imported (<\\/span>Import_2_OSM value of 0).
<\\/div>

<\\/div>
Features with an Import_2_OSM value of 1 (i.e. Import <\\/b><\\/font>features, shown in Green<\\/b><\\/font>) have no conflict and can be added to OSM.  Features with an Import_2_OSM value of 0 (i.e. No Import<\\/font><\\/b> features, shown in Red<\\/b><\\/font>) conflict with existing buildings and will not be added.<\\/div>

<\\/div>
Other fields<\\/b><\\/div>