├── testutils ├── build.gradle └── src │ └── main │ └── java │ └── de │ └── westnordost │ └── osmapi │ ├── TestUtils.java │ └── ConnectionTestFactory.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── .gitignore ├── libs ├── messages │ ├── src │ │ ├── main │ │ │ └── java │ │ │ │ └── de │ │ │ │ └── westnordost │ │ │ │ └── osmapi │ │ │ │ └── messages │ │ │ │ ├── MessageOrder.java │ │ │ │ ├── Message.java │ │ │ │ └── MessagesParser.java │ │ └── test │ │ │ └── java │ │ │ └── de │ │ │ └── westnordost │ │ │ └── osmapi │ │ │ └── messages │ │ │ └── MessagesParserTest.java │ └── build.gradle ├── map │ ├── src │ │ ├── main │ │ │ └── java │ │ │ │ └── de │ │ │ │ └── westnordost │ │ │ │ └── osmapi │ │ │ │ └── map │ │ │ │ ├── data │ │ │ │ ├── Node.java │ │ │ │ ├── Way.java │ │ │ │ ├── Relation.java │ │ │ │ ├── RelationMember.java │ │ │ │ ├── OsmTags.java │ │ │ │ ├── Element.java │ │ │ │ ├── OsmWay.java │ │ │ │ ├── OsmRelation.java │ │ │ │ ├── OsmNode.java │ │ │ │ ├── OsmRelationMember.java │ │ │ │ ├── OsmElement.java │ │ │ │ └── ModificationAwareMap.java │ │ │ │ ├── changes │ │ │ │ ├── MapDataChangesHandler.java │ │ │ │ ├── MapDataChanges.java │ │ │ │ ├── DiffElement.java │ │ │ │ ├── MapDataChangesParser.java │ │ │ │ ├── MapDataDiffParser.java │ │ │ │ └── SimpleMapDataChangesHandler.java │ │ │ │ ├── handler │ │ │ │ ├── MapDataHandler.java │ │ │ │ ├── SingleOsmElementHandler.java │ │ │ │ ├── WrapperOsmElementHandler.java │ │ │ │ ├── ListOsmElementHandler.java │ │ │ │ ├── DefaultMapDataHandler.java │ │ │ │ └── OneElementTypeHandler.java │ │ │ │ ├── OsmMapDataFactory.java │ │ │ │ └── MapDataFactory.java │ │ └── test │ │ │ └── java │ │ │ └── de │ │ │ └── westnordost │ │ │ └── osmapi │ │ │ └── map │ │ │ ├── ElementWithoutTagsTest.java │ │ │ ├── ElementShouldExist.java │ │ │ ├── data │ │ │ └── OsmRelationMemberTest.java │ │ │ ├── changes │ │ │ ├── MapDataDiffParserTest.java │ │ │ └── MapDataChangesParserTest.java │ │ │ └── MapDataHistoryApiTest.java │ └── build.gradle ├── core │ ├── src │ │ ├── main │ │ │ └── java │ │ │ │ └── de │ │ │ │ └── westnordost │ │ │ │ └── osmapi │ │ │ │ ├── common │ │ │ │ ├── Handler.java │ │ │ │ ├── errors │ │ │ │ │ ├── RedirectedException.java │ │ │ │ │ ├── OsmBadUserInputException.java │ │ │ │ │ ├── OsmApiReadResponseException.java │ │ │ │ │ ├── OsmNotFoundException.java │ │ │ │ │ ├── OsmTooManyRequestsException.java │ │ │ │ │ ├── OsmServiceUnavailableException.java │ │ │ │ │ ├── XmlParserException.java │ │ │ │ │ ├── OsmConflictException.java │ │ │ │ │ ├── OsmQueryTooBigException.java │ │ │ │ │ ├── OsmAuthorizationException.java │ │ │ │ │ ├── OsmPreconditionFailedException.java │ │ │ │ │ ├── OsmApiException.java │ │ │ │ │ └── OsmConnectionException.java │ │ │ │ ├── ListHandler.java │ │ │ │ ├── SingleElementHandler.java │ │ │ │ ├── PlainTextWriter.java │ │ │ │ ├── IdResponseReader.java │ │ │ │ ├── FormDataWriter.java │ │ │ │ ├── XmlWriter.java │ │ │ │ └── XmlParser.java │ │ │ │ ├── map │ │ │ │ └── data │ │ │ │ │ ├── LatLon.java │ │ │ │ │ ├── Fixed1E7.java │ │ │ │ │ ├── LatLons.java │ │ │ │ │ ├── Fixed1E7LatLon.java │ │ │ │ │ ├── OsmLatLon.java │ │ │ │ │ └── BoundingBox.java │ │ │ │ ├── ApiRequestWriter.java │ │ │ │ ├── changesets │ │ │ │ └── Changeset.java │ │ │ │ ├── user │ │ │ │ ├── User.java │ │ │ │ ├── PermissionsApi.java │ │ │ │ ├── Permission.java │ │ │ │ └── PermissionsParser.java │ │ │ │ ├── ApiResponseReader.java │ │ │ │ ├── capabilities │ │ │ │ ├── CapabilitiesApi.java │ │ │ │ ├── Capabilities.java │ │ │ │ └── CapabilitiesParser.java │ │ │ │ └── OsmApiErrorFactory.java │ │ └── test │ │ │ └── java │ │ │ └── de │ │ │ └── westnordost │ │ │ └── osmapi │ │ │ ├── capabilities │ │ │ ├── CapabilitiesApiTest.java │ │ │ └── CapabilitiesParserTest.java │ │ │ ├── user │ │ │ ├── PermissionsParserTest.java │ │ │ └── PermissionsApiTest.java │ │ │ ├── OsmApiErrorFactoryTest.java │ │ │ ├── OsmConnectionTest.java │ │ │ ├── map │ │ │ └── data │ │ │ │ ├── BoundingBoxTest.java │ │ │ │ └── OsmLatLonTest.java │ │ │ └── common │ │ │ ├── XmlWriterTest.java │ │ │ └── FormDataWriterTest.java │ └── build.gradle ├── user │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── de │ │ │ └── westnordost │ │ │ └── osmapi │ │ │ └── user │ │ │ ├── UserRole.java │ │ │ ├── UserSocialLink.java │ │ │ ├── UserDetails.java │ │ │ ├── PreferencesParser.java │ │ │ ├── UserInfo.java │ │ │ ├── UserApi.java │ │ │ ├── UserDetailsParser.java │ │ │ └── UserInfoParser.java │ │ └── test │ │ └── java │ │ └── de │ │ └── westnordost │ │ └── osmapi │ │ └── user │ │ ├── PreferencesParserTest.java │ │ ├── UserApiTest.java │ │ ├── UserPreferencesApiTest.java │ │ └── UserDetailsParserTest.java ├── notes │ ├── build.gradle │ └── src │ │ ├── test │ │ └── java │ │ │ └── de │ │ │ └── westnordost │ │ │ └── osmapi │ │ │ └── notes │ │ │ └── NotesDateFormatTest.java │ │ └── main │ │ └── java │ │ └── de │ │ └── westnordost │ │ └── osmapi │ │ └── notes │ │ ├── NoteComment.java │ │ ├── NotesDateFormat.java │ │ ├── Note.java │ │ ├── NotesParser.java │ │ └── QueryNotesFilters.java ├── traces │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── de │ │ │ └── westnordost │ │ │ └── osmapi │ │ │ └── traces │ │ │ ├── GpxInputStreamWriter.java │ │ │ ├── GpsTraceDetails.java │ │ │ ├── GpsTrackpoint.java │ │ │ ├── GpsTraceWriter.java │ │ │ ├── GpsTracesParser.java │ │ │ ├── GpxTrackParser.java │ │ │ └── GpxTrackWriter.java │ │ └── test │ │ ├── java │ │ └── de │ │ │ └── westnordost │ │ │ └── osmapi │ │ │ └── traces │ │ │ ├── GpsTracesWriterTest.java │ │ │ ├── GpxTrackParserTest.java │ │ │ ├── GpxTrackWriterTest.java │ │ │ └── GpsTracesParserTest.java │ │ └── resources │ │ └── track.gpx ├── changesets │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── de │ │ └── westnordost │ │ └── osmapi │ │ └── changesets │ │ ├── ChangesetNote.java │ │ ├── ChangesetInfo.java │ │ └── ChangesetParser.java ├── all │ └── build.gradle └── build.gradle ├── settings.gradle ├── .github └── workflows │ └── gradle-wrapper-validation.yml ├── gradlew.bat └── Combine_With_Data_Processing_Libraries.md /testutils/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile "de.westnordost:osmapi-core:$core_version" 3 | } 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/westnordost/osmapi/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | signing.keyId=08A11DBC 2 | signing.password= 3 | signing.secretKeyRingFile= 4 | 5 | ossrhUsername= 6 | ossrhPassword= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | .gradle 3 | build 4 | .project 5 | .classpath 6 | .settings 7 | gradle-app.setting 8 | /.idea/ 9 | /local.properties 10 | *.class -------------------------------------------------------------------------------- /libs/messages/src/main/java/de/westnordost/osmapi/messages/MessageOrder.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.messages; 2 | 3 | public enum MessageOrder { 4 | NEWEST, 5 | OLDEST 6 | } -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/Node.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | public interface Node extends Element 4 | { 5 | LatLon getPosition(); 6 | } 7 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/Way.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.util.List; 4 | 5 | public interface Way extends Element 6 | { 7 | List getNodeIds(); 8 | } 9 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/Handler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common; 2 | 3 | public interface Handler 4 | { 5 | /** Called when a new object is created from the input stream */ 6 | void handle(T tea); 7 | } 8 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/Relation.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.util.List; 4 | 5 | public interface Relation extends Element 6 | { 7 | List getMembers(); 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':libs:core' 2 | include ':libs:changesets' 3 | include ':libs:map' 4 | include ':libs:notes' 5 | include ':libs:traces' 6 | include ':libs:user' 7 | include ':libs:messages' 8 | include ':libs:all' 9 | include ':testutils' 10 | 11 | -------------------------------------------------------------------------------- /libs/user/build.gradle: -------------------------------------------------------------------------------- 1 | archivesBaseName = "osmapi-user" 2 | version = user_version 3 | description = 'Client for the OSM API 0.6 - Managing user preferences, getting user information' 4 | 5 | dependencies { 6 | compile "de.westnordost:osmapi-core:$core_version" 7 | } 8 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/map/data/LatLon.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | /** 4 | * A geo position (without height) 5 | */ 6 | public interface LatLon 7 | { 8 | double getLatitude(); 9 | 10 | double getLongitude(); 11 | } 12 | -------------------------------------------------------------------------------- /libs/notes/build.gradle: -------------------------------------------------------------------------------- 1 | archivesBaseName = "osmapi-notes" 2 | version = notes_version 3 | description = 'Client for the OSM API 0.6 - Getting finding, creating, commenting on and solving notes' 4 | 5 | dependencies { 6 | compile "de.westnordost:osmapi-core:$core_version" 7 | } 8 | -------------------------------------------------------------------------------- /libs/traces/build.gradle: -------------------------------------------------------------------------------- 1 | archivesBaseName = "osmapi-traces" 2 | version = traces_version 3 | description = 'Client for the OSM API 0.6 - Getting, uploading, updating and deleting GPS traces and trackpoints' 4 | 5 | dependencies { 6 | compile "de.westnordost:osmapi-core:$core_version" 7 | } 8 | -------------------------------------------------------------------------------- /libs/messages/build.gradle: -------------------------------------------------------------------------------- 1 | archivesBaseName = "osmapi-messages" 2 | version = messages_version 3 | description = 'Client for the OSM API 0.6 - Sending and reading messages' 4 | 5 | dependencies { 6 | compile "de.westnordost:osmapi-core:$core_version" 7 | compile "de.westnordost:osmapi-user:$user_version" 8 | } 9 | -------------------------------------------------------------------------------- /libs/core/build.gradle: -------------------------------------------------------------------------------- 1 | archivesBaseName = "osmapi-core" 2 | version = core_version 3 | description = 'Client for the OSM API 0.6 - Core functionality, getting server capabilities, getting user permissions' 4 | 5 | dependencies { 6 | compile 'xmlpull:xmlpull:1.1.3.1' 7 | runtime 'net.sf.kxml:kxml2:2.3.0' 8 | } 9 | -------------------------------------------------------------------------------- /libs/changesets/build.gradle: -------------------------------------------------------------------------------- 1 | archivesBaseName = "osmapi-changesets" 2 | version = changesets_version 3 | description = 'Client for the OSM API 0.6 - Finding changesets, changeset discussion, subscription and data' 4 | 5 | dependencies { 6 | compile "de.westnordost:osmapi-core:$core_version" 7 | compile "de.westnordost:osmapi-map:$map_version" 8 | } 9 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/RelationMember.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | public interface RelationMember 4 | { 5 | String getRole(); 6 | 7 | /** @return id of the element this object refers to */ 8 | long getRef(); 9 | 10 | Element.Type getType(); 11 | 12 | boolean isModified(); 13 | } 14 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/changes/MapDataChangesHandler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.changes; 2 | 3 | import de.westnordost.osmapi.map.handler.MapDataHandler; 4 | 5 | public interface MapDataChangesHandler extends MapDataHandler 6 | { 7 | void onStartCreations(); 8 | void onStartModifications(); 9 | void onStartDeletions(); 10 | } 11 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/RedirectedException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | import java.io.IOException; 4 | 5 | /** Thrown when the ISP / Wifi router redirected us to someplace else (i.e. some login page)*/ 6 | public class RedirectedException extends IOException 7 | { 8 | private static final long serialVersionUID = 1L; 9 | } 10 | -------------------------------------------------------------------------------- /libs/user/src/main/java/de/westnordost/osmapi/user/UserRole.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | /** Simply some constants for known user roles */ 4 | public final class UserRole 5 | { 6 | public static final String ADMINISTRATOR = "administrator"; 7 | public static final String MODERATOR = "moderator"; 8 | 9 | private UserRole() 10 | { 11 | // not instantiable 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/changes/MapDataChanges.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.changes; 2 | 3 | import java.util.List; 4 | 5 | import de.westnordost.osmapi.map.data.Element; 6 | 7 | public interface MapDataChanges 8 | { 9 | List getDeletions(); 10 | List getModifications(); 11 | List getCreations(); 12 | List getAll(); 13 | } 14 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/ApiRequestWriter.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | /** 7 | * Handles writing data to an API request. Assumed to use the charset UTF-8 8 | */ 9 | public interface ApiRequestWriter 10 | { 11 | String getContentType(); 12 | 13 | void write(OutputStream out) throws IOException; 14 | } 15 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmBadUserInputException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | public class OsmBadUserInputException extends OsmApiException 4 | { 5 | private static final long serialVersionUID = 1L; 6 | 7 | public OsmBadUserInputException(int errorCode, String errorTitle, String description) 8 | { 9 | super(errorCode, errorTitle, description); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/changesets/Changeset.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.changesets; 2 | 3 | import java.io.Serializable; 4 | 5 | import de.westnordost.osmapi.user.User; 6 | 7 | /** 8 | * Short info for a changeset. 9 | */ 10 | public class Changeset implements Serializable 11 | { 12 | private static final long serialVersionUID = 2L; 13 | 14 | public long id; 15 | public User user; 16 | } 17 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmApiReadResponseException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** An error occured while parsing the response of an Osm Api call */ 4 | public class OsmApiReadResponseException extends RuntimeException 5 | { 6 | private static final long serialVersionUID = 1L; 7 | 8 | public OsmApiReadResponseException(Exception e) 9 | { 10 | super(e); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /libs/map/build.gradle: -------------------------------------------------------------------------------- 1 | archivesBaseName = "osmapi-map" 2 | version = map_version 3 | description = 'Client for the OSM API 0.6 - Getting map data, querying single elements and their relations toward each other and uploading changes in changesets, getting the history and specific versions of elements' 4 | 5 | dependencies { 6 | compile "de.westnordost:osmapi-core:$core_version" 7 | testCompile "de.westnordost:osmapi-changesets:$changesets_version" 8 | } 9 | -------------------------------------------------------------------------------- /libs/user/src/main/java/de/westnordost/osmapi/user/UserSocialLink.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | /** Link to the profile page of a user on a social platform. */ 4 | public class UserSocialLink implements Serializable 5 | { 6 | private static final long serialVersionUID = 1L; 7 | 8 | /** The social platform. Might be null or empty if not detected. */ 9 | public String platform; 10 | 11 | /** The URL */ 12 | public String url; 13 | } -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/user/User.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import java.io.Serializable; 4 | 5 | /** Short info for a user */ 6 | public class User implements Serializable 7 | { 8 | private static final long serialVersionUID = 1L; 9 | 10 | public long id; 11 | public String displayName; 12 | 13 | public User(long id, String displayName) 14 | { 15 | this.id = id; 16 | this.displayName = displayName; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmNotFoundException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** Thrown when an action fails because the element does not exist */ 4 | public class OsmNotFoundException extends OsmApiException 5 | { 6 | private static final long serialVersionUID = 1L; 7 | 8 | public OsmNotFoundException(int errorCode, String errorTitle, String description) 9 | { 10 | super(errorCode, errorTitle, description); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper-validation.yml: -------------------------------------------------------------------------------- 1 | name: "Validate Gradle Wrapper" 2 | on: 3 | push: 4 | paths: 5 | - 'gradle/wrapper/**' 6 | - 'gradlew' 7 | - 'gradlew.bat' 8 | pull_request: 9 | paths: 10 | - 'gradle/wrapper/**' 11 | - 'gradlew' 12 | - 'gradlew.bat' 13 | 14 | jobs: 15 | validation: 16 | name: "Validation" 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: gradle/wrapper-validation-action@v1 -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/ListHandler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** Puts all the elements into a list*/ 7 | public class ListHandler implements Handler 8 | { 9 | private List list = new ArrayList<>(); 10 | 11 | @Override 12 | public void handle(T tea) 13 | { 14 | list.add(tea); 15 | } 16 | 17 | public List get() 18 | { 19 | return list; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmTooManyRequestsException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** When the request has been blocked due to rate limiting */ 4 | public class OsmTooManyRequestsException extends OsmApiException { 5 | private static final long serialVersionUID = 1L; 6 | 7 | public OsmTooManyRequestsException(int errorCode, String errorTitle, String description) 8 | { 9 | super(errorCode, errorTitle, description); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /libs/all/build.gradle: -------------------------------------------------------------------------------- 1 | archivesBaseName = "osmapi" 2 | version = all_version 3 | description = 'Client for the OSM API 0.6' 4 | 5 | dependencies { 6 | compile "de.westnordost:osmapi-core:$core_version" 7 | compile "de.westnordost:osmapi-changesets:$changesets_version" 8 | compile "de.westnordost:osmapi-map:$map_version" 9 | compile "de.westnordost:osmapi-notes:$notes_version" 10 | compile "de.westnordost:osmapi-traces:$traces_version" 11 | compile "de.westnordost:osmapi-user:$user_version" 12 | } 13 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/SingleElementHandler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common; 2 | 3 | 4 | /** Handler that expects just a single element. It can be queried via get(). */ 5 | public class SingleElementHandler implements Handler 6 | { 7 | private T tea = null; 8 | 9 | @Override 10 | public synchronized void handle(T tea) 11 | { 12 | this.tea = tea; 13 | } 14 | 15 | /** @return the element */ 16 | public synchronized T get() 17 | { 18 | return tea; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /libs/core/src/test/java/de/westnordost/osmapi/capabilities/CapabilitiesApiTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.capabilities; 2 | 3 | import org.junit.Test; 4 | 5 | import de.westnordost.osmapi.ConnectionTestFactory; 6 | import de.westnordost.osmapi.OsmConnection; 7 | 8 | public class CapabilitiesApiTest 9 | { 10 | @Test public void capabilities() 11 | { 12 | OsmConnection c = ConnectionTestFactory.createConnection(null); 13 | new CapabilitiesApi(c).get(); 14 | // as the response may vary, it should just not throw an exception 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/OsmTags.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.io.Serializable; 4 | import java.util.Map; 5 | 6 | /** Represent the tags for any osm element. It is a Map of Strings which registers whether it has been modified */ 7 | public class OsmTags extends ModificationAwareMap implements Serializable 8 | { 9 | private static final long serialVersionUID = 1L; 10 | 11 | public OsmTags(Map map) 12 | { 13 | super(map); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /libs/map/src/test/java/de/westnordost/osmapi/map/ElementWithoutTagsTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map; 2 | 3 | import org.junit.Test; 4 | 5 | import de.westnordost.osmapi.map.data.OsmLatLon; 6 | import de.westnordost.osmapi.map.data.OsmNode; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | public class ElementWithoutTagsTest 11 | { 12 | @Test public void elementWithoutTags() 13 | { 14 | OsmNode node = new OsmNode(1, 1, new OsmLatLon(1, 1), null, null, null); 15 | assertEquals(node.getTags().size(), 0); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/handler/MapDataHandler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.handler; 2 | 3 | import de.westnordost.osmapi.map.data.BoundingBox; 4 | import de.westnordost.osmapi.map.data.Node; 5 | import de.westnordost.osmapi.map.data.Relation; 6 | import de.westnordost.osmapi.map.data.Way; 7 | 8 | /** This class is fed the map data. */ 9 | public interface MapDataHandler 10 | { 11 | void handle(BoundingBox bounds); 12 | 13 | void handle(Node node); 14 | void handle(Way way); 15 | void handle(Relation relation); 16 | } 17 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmServiceUnavailableException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** Thrown when an API request failed because the server responded that the service is not 4 | * available (i.e. database offline)*/ 5 | public class OsmServiceUnavailableException extends OsmConnectionException 6 | { 7 | private static final long serialVersionUID = 1L; 8 | 9 | public OsmServiceUnavailableException(int errorCode, String errorTitle, String description) 10 | { 11 | super(errorCode, errorTitle, description); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/handler/SingleOsmElementHandler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.handler; 2 | 3 | /** Handler that expects just one element of the given type */ 4 | public class SingleOsmElementHandler extends OneElementTypeHandler 5 | { 6 | private T element = null; 7 | 8 | public SingleOsmElementHandler(Class tClass) 9 | { 10 | super(tClass); 11 | } 12 | 13 | @Override 14 | protected void handleElement(T element) 15 | { 16 | this.element = element; 17 | } 18 | 19 | public T get() 20 | { 21 | return element; 22 | } 23 | } -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/Element.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.time.Instant; 4 | import java.util.Map; 5 | 6 | import de.westnordost.osmapi.changesets.Changeset; 7 | 8 | public interface Element 9 | { 10 | enum Type 11 | { 12 | NODE, WAY, RELATION 13 | } 14 | 15 | boolean isNew(); 16 | 17 | boolean isModified(); 18 | 19 | boolean isDeleted(); 20 | 21 | long getId(); 22 | 23 | int getVersion(); 24 | 25 | Changeset getChangeset(); 26 | 27 | Instant getEditedAt(); 28 | 29 | Map getTags(); 30 | 31 | Type getType(); 32 | } 33 | -------------------------------------------------------------------------------- /libs/notes/src/test/java/de/westnordost/osmapi/notes/NotesDateFormatTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.notes; 2 | 3 | import org.junit.Test; 4 | 5 | import java.time.ZoneId; 6 | import java.time.ZonedDateTime; 7 | import java.time.format.DateTimeParseException; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | public class NotesDateFormatTest 12 | { 13 | @Test public void date() throws DateTimeParseException 14 | { 15 | assertEquals( 16 | ZonedDateTime.of(2020, 10, 26, 19, 33, 14, 0, ZoneId.of("UTC")).toInstant(), 17 | new NotesDateFormat().parse("2020-10-26 19:33:14 UTC") 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/ApiResponseReader.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi; 2 | 3 | import java.io.InputStream; 4 | 5 | /** 6 | * Handles the response from the API: Parses whatever comes through the input stream and returns 7 | * a result. Assumed to use the charset UTF-8 8 | * 9 | * @param Parsing result type 10 | */ 11 | public interface ApiResponseReader 12 | { 13 | /** Called when the input stream is available. 14 | * 15 | * @param in the input stream from the server response 16 | * @return the result of the parsing process */ 17 | T parse(InputStream in) throws Exception; 18 | } 19 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/handler/WrapperOsmElementHandler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.handler; 2 | 3 | import de.westnordost.osmapi.common.Handler; 4 | 5 | /** Wraps a Handler in a MapDataHandler */ 6 | public class WrapperOsmElementHandler extends OneElementTypeHandler 7 | { 8 | private final Handler handler; 9 | 10 | public WrapperOsmElementHandler(Class tClass, Handler handler) 11 | { 12 | super(tClass); 13 | this.handler = handler; 14 | } 15 | 16 | @Override 17 | protected void handleElement(T element) 18 | { 19 | handler.handle(element); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/capabilities/CapabilitiesApi.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.capabilities; 2 | 3 | import de.westnordost.osmapi.OsmConnection; 4 | 5 | /** Get server capabilities */ 6 | public class CapabilitiesApi 7 | { 8 | private final OsmConnection osm; 9 | 10 | public CapabilitiesApi(OsmConnection osm) 11 | { 12 | this.osm = osm; 13 | } 14 | 15 | /** @return the capabilities and limits of this server. This usually does not change very 16 | * often for a given server. */ 17 | public Capabilities get() 18 | { 19 | return osm.makeRequest("capabilities", new CapabilitiesParser()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libs/changesets/src/main/java/de/westnordost/osmapi/changesets/ChangesetNote.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.changesets; 2 | 3 | 4 | import java.io.Serializable; 5 | import java.time.Instant; 6 | 7 | import de.westnordost.osmapi.user.User; 8 | 9 | /** A post in the changeset discussion. Avoided the wording "changeset comment" here, because this 10 | * is already what the "commit message" is called in editors */ 11 | public class ChangesetNote implements Serializable 12 | { 13 | private static final long serialVersionUID = 3L; 14 | 15 | public Long id; 16 | public Instant createdAt; 17 | public User user; 18 | public String text; 19 | } 20 | -------------------------------------------------------------------------------- /libs/map/src/test/java/de/westnordost/osmapi/map/ElementShouldExist.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map; 2 | 3 | /** A static collection of elements that should exist in the OSM database. If not, this needs to be 4 | * updated to be something that exists for the tests to work. 5 | * 6 | * The tests simply need something where it is known that the API will return something */ 7 | public class ElementShouldExist 8 | { 9 | public static final long NODE = 26576175L, // Yangon (place node) 10 | WAY = 23564402L, // some harbour area in Hamburg 11 | RELATION = 180627L; // a quarter in Hamburg 12 | } 13 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/XmlParserException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** Wraps any exception that occurs during parsing with the XmlParser */ 4 | public class XmlParserException extends RuntimeException 5 | { 6 | private static final long serialVersionUID = 1L; 7 | 8 | public XmlParserException(String positionDescription, Throwable cause) 9 | { 10 | super("Error parsing XML at " + positionDescription, cause); 11 | } 12 | 13 | public XmlParserException(Throwable cause) 14 | { 15 | super(cause); 16 | } 17 | 18 | public XmlParserException(String reason) 19 | { 20 | super(reason); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/handler/ListOsmElementHandler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.handler; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** Handler that expects a number of elements of the given type */ 7 | public class ListOsmElementHandler extends OneElementTypeHandler 8 | { 9 | private final List result = new ArrayList<>(); 10 | 11 | public ListOsmElementHandler(Class tClass) 12 | { 13 | super(tClass); 14 | } 15 | 16 | @Override 17 | protected void handleElement(T element) 18 | { 19 | result.add(element); 20 | } 21 | 22 | public List get() 23 | { 24 | return result; 25 | } 26 | } -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmConflictException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** Thrown when an action fails because the precondition changed on server side. I.e. trying to 4 | * close a note that has already been closed, change an element whose version number already 5 | * increased, change or close a changeset that has already been closed etc. 6 | */ 7 | public class OsmConflictException extends OsmApiException 8 | { 9 | private static final long serialVersionUID = 1L; 10 | 11 | public OsmConflictException(int errorCode, String errorTitle, String description) 12 | { 13 | super(errorCode, errorTitle, description); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmQueryTooBigException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** Thrown when trying to request an area that is either too big or contains too much data. */ 4 | public class OsmQueryTooBigException extends OsmBadUserInputException 5 | { 6 | private static final long serialVersionUID = 1L; 7 | 8 | public OsmQueryTooBigException(OsmApiException other) 9 | { 10 | super(other.getErrorCode(), other.getErrorTitle(), other.getDescription()); 11 | } 12 | 13 | public OsmQueryTooBigException(int responseCode, String responseBody, String description) 14 | { 15 | super(responseCode, responseBody, description); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/PlainTextWriter.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | import de.westnordost.osmapi.ApiRequestWriter; 7 | 8 | public class PlainTextWriter implements ApiRequestWriter 9 | { 10 | private static final String CHARSET = "UTF-8"; 11 | 12 | private String data; 13 | 14 | public PlainTextWriter(String data) 15 | { 16 | this.data = data; 17 | } 18 | 19 | @Override 20 | public String getContentType() 21 | { 22 | return "text/plain"; 23 | } 24 | 25 | public void write(OutputStream out) throws IOException 26 | { 27 | out.write( data.getBytes(CHARSET) ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/user/PermissionsApi.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import java.util.List; 4 | 5 | import de.westnordost.osmapi.OsmConnection; 6 | 7 | /** Get the user permissions */ 8 | public class PermissionsApi 9 | { 10 | private final OsmConnection osm; 11 | 12 | public PermissionsApi(OsmConnection osm) 13 | { 14 | this.osm = osm; 15 | } 16 | 17 | /** @return a list of permissions the user has on this server (=are granted though OAuth). Use 18 | * the constants defined in the Permission, i.e Permission.CHANGE_PREFERENCES */ 19 | public List get() 20 | { 21 | return osm.makeAuthenticatedRequest("permissions", "GET", new PermissionsParser()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/handler/DefaultMapDataHandler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.handler; 2 | 3 | import de.westnordost.osmapi.map.data.BoundingBox; 4 | import de.westnordost.osmapi.map.data.Node; 5 | import de.westnordost.osmapi.map.data.Relation; 6 | import de.westnordost.osmapi.map.data.Way; 7 | 8 | /** 9 | * Empty implementation of the map data handler 10 | */ 11 | public class DefaultMapDataHandler implements MapDataHandler 12 | { 13 | @Override 14 | public void handle(BoundingBox bounds) {} 15 | 16 | @Override 17 | public void handle(Node node) {} 18 | 19 | @Override 20 | public void handle(Way way) {} 21 | 22 | @Override 23 | public void handle(Relation relation) {} 24 | } 25 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmAuthorizationException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** Thrown when the user is blocked, did not agree to the terms, his OAuth token does not have this 4 | * capability, he has not the necessary rights (i.e. a moderator action) or is not authenticated 5 | * at all */ 6 | public class OsmAuthorizationException extends OsmApiException 7 | { 8 | private static final long serialVersionUID = 1L; 9 | 10 | public OsmAuthorizationException(Throwable cause) 11 | { 12 | super(cause); 13 | } 14 | 15 | public OsmAuthorizationException(int errorCode, String errorTitle, String description) 16 | { 17 | super(errorCode, errorTitle, description); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmPreconditionFailedException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** Thrown when the user is blocked, did not agree to the terms, his OAuth token does not have this 4 | * capability, he has not the necessary rights (i.e. a moderator action) or is not authenticated 5 | * at all */ 6 | public class OsmPreconditionFailedException extends OsmApiException 7 | { 8 | private static final long serialVersionUID = 1L; 9 | 10 | public OsmPreconditionFailedException(Throwable cause) 11 | { 12 | super(cause); 13 | } 14 | 15 | public OsmPreconditionFailedException(int errorCode, String errorTitle, String description) 16 | { 17 | super(errorCode, errorTitle, description); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/map/data/Fixed1E7.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | /** 4 | * Fixed point number math with 7 decimal places. 5 | */ 6 | public class Fixed1E7 7 | { 8 | private static final int DECIMAL_PLACES = 7; 9 | private static final int FIXED = (int) Math.pow(10, DECIMAL_PLACES); 10 | 11 | public static int parseFixed(String str) 12 | { 13 | return doubleToFixed(Double.parseDouble(str)); 14 | } 15 | 16 | public static int intToFixed(int n) 17 | { 18 | return n * FIXED; 19 | } 20 | 21 | public static int doubleToFixed(double value) { return (int) Math.round(FIXED * value); } 22 | 23 | public static double toDouble(int value) 24 | { 25 | return (double) value / FIXED; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/changes/DiffElement.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.changes; 2 | 3 | import java.io.Serializable; 4 | 5 | import de.westnordost.osmapi.map.data.Element; 6 | 7 | public class DiffElement implements Serializable 8 | { 9 | private static final long serialVersionUID = 1L; 10 | 11 | public Element.Type type; 12 | /** aka old_id: the (placeholder) id the element had when the client sent it. */ 13 | public long clientId; 14 | /** aka new_id: the id the element now has on the server. Can be null if it i.e. a deleted element*/ 15 | public Long serverId; 16 | /** aka new_version: the new version element now has on the server. Can be null if it i.e. a deleted element */ 17 | public Integer serverVersion; 18 | } 19 | -------------------------------------------------------------------------------- /libs/traces/src/main/java/de/westnordost/osmapi/traces/GpxInputStreamWriter.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | import de.westnordost.osmapi.ApiRequestWriter; 8 | 9 | public class GpxInputStreamWriter implements ApiRequestWriter 10 | { 11 | private InputStream gpx; 12 | 13 | public GpxInputStreamWriter(InputStream in) 14 | { 15 | this.gpx = in; 16 | } 17 | 18 | @Override 19 | public String getContentType() 20 | { 21 | return "application/gpx+xml"; 22 | } 23 | 24 | public void write(OutputStream out) throws IOException 25 | { 26 | byte[] buffer = new byte[8192]; 27 | int length; 28 | while ((length = gpx.read(buffer)) != -1) out.write(buffer, 0, length); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /libs/notes/src/main/java/de/westnordost/osmapi/notes/NoteComment.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.notes; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | import de.westnordost.osmapi.user.User; 7 | 8 | /** A note comment from the osm notes api */ 9 | public class NoteComment implements Serializable 10 | { 11 | private static final long serialVersionUID = 2L; 12 | 13 | public Instant date; 14 | public Action action; 15 | public String text; 16 | /** the user who wrote the comment. May be null if the user is anonymous */ 17 | public User user; 18 | 19 | public boolean isAnonymous() 20 | { 21 | return user == null; 22 | } 23 | 24 | public enum Action 25 | { 26 | OPENED, 27 | COMMENTED, 28 | CLOSED, 29 | REOPENED, 30 | HIDDEN 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /libs/notes/src/main/java/de/westnordost/osmapi/notes/NotesDateFormat.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.notes; 2 | 3 | import java.time.Instant; 4 | import java.time.ZonedDateTime; 5 | import java.time.format.DateTimeFormatter; 6 | import java.time.format.DateTimeParseException; 7 | 8 | /** The date format for the notes API is a little different: The 'T' literal is missing between 9 | * time and date */ 10 | public class NotesDateFormat 11 | { 12 | private final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss z"); 13 | 14 | public Instant parse(String source) throws DateTimeParseException 15 | { 16 | return ZonedDateTime.parse(source, FORMATTER).toInstant(); 17 | } 18 | 19 | public String format(Instant instant) 20 | { 21 | return FORMATTER.format(instant); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /libs/messages/src/main/java/de/westnordost/osmapi/messages/Message.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.messages; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | import de.westnordost.osmapi.user.User; 7 | 8 | /** An OpenStreetMap message */ 9 | public class Message implements Serializable 10 | { 11 | private static final long serialVersionUID = 1L; 12 | 13 | public long id; 14 | public User fromUser; 15 | public User toUser; 16 | public String title; 17 | public String body; 18 | public BodyFormat bodyFormat; 19 | public Instant sentOn; 20 | /** Whether the message has been read. Only non-null for messages sent to the current user */ 21 | public Boolean read; 22 | public boolean deleted; 23 | 24 | public enum BodyFormat { 25 | TEXT, 26 | MARKDOWN, 27 | HTML 28 | } 29 | } -------------------------------------------------------------------------------- /libs/core/src/test/java/de/westnordost/osmapi/user/PermissionsParserTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.IOException; 6 | import java.util.List; 7 | 8 | import de.westnordost.osmapi.TestUtils; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | public class PermissionsParserTest 13 | { 14 | @Test public void permissionsParser() throws IOException 15 | { 16 | String xml = 17 | "" + 18 | " " + 19 | " " + 20 | ""; 21 | 22 | List permissions = new PermissionsParser().parse(TestUtils.asInputStream(xml)); 23 | assertTrue(permissions.contains("allow_xyz")); 24 | assertTrue(permissions.contains("allow_abc")); 25 | assertFalse(permissions.contains("allow_somethingElse")); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/user/Permission.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | /** Simply some constants for known permissions. See 4 | * https://github.com/openstreetmap/openstreetmap-website/blob/master/db/structure.sql#L1108 */ 5 | public final class Permission 6 | { 7 | public static final String 8 | READ_PREFERENCES_AND_USER_DETAILS = "allow_read_prefs", 9 | CHANGE_PREFERENCES = "allow_write_prefs", 10 | WRITE_DIARY = "allow_write_diary", 11 | MODIFY_MAP = "allow_write_api", 12 | READ_GPS_TRACES = "allow_read_gpx", 13 | WRITE_GPS_TRACES = "allow_write_gpx", 14 | WRITE_NOTES = "allow_write_notes", 15 | CONSUME_MESSAGES = "allow_consume_messages", 16 | SEND_MESSAGES = "allow_send_messages", 17 | WRITE_REDACTIONS = "allow_write_redactions"; 18 | 19 | private Permission() 20 | { 21 | // not instantiable 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /libs/user/src/test/java/de/westnordost/osmapi/user/PreferencesParserTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.IOException; 6 | import java.util.Map; 7 | 8 | import de.westnordost.osmapi.TestUtils; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class PreferencesParserTest 13 | { 14 | @Test public void preferencesParser() throws IOException 15 | { 16 | String xml = 17 | "" + 18 | " " + 19 | " " + 20 | ""; 21 | 22 | Map preferences = new PreferencesParser().parse(TestUtils.asInputStream(xml)); 23 | assertEquals("identifiable", preferences.get("gps.trace.visibility")); 24 | assertEquals("true",preferences.get("something.else")); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/IdResponseReader.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | 7 | import de.westnordost.osmapi.ApiResponseReader; 8 | 9 | /** Parses an id response sent via plain text by the server */ 10 | public class IdResponseReader implements ApiResponseReader 11 | { 12 | private static final String CHARSET = "UTF-8"; 13 | 14 | /** size of a stream buffer to accommodate any long value send as text/plain */ 15 | private static final int BUFFER_SIZE = String.valueOf(Long.MAX_VALUE).length(); 16 | 17 | public Long parse(InputStream in) throws Exception 18 | { 19 | BufferedReader reader = new BufferedReader( 20 | new InputStreamReader(in, CHARSET), BUFFER_SIZE 21 | ); 22 | return Long.parseLong(reader.readLine()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /testutils/src/main/java/de/westnordost/osmapi/TestUtils.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.InputStream; 6 | import java.io.UnsupportedEncodingException; 7 | 8 | public class TestUtils 9 | { 10 | private static final String CHARSET = "UTF-8"; 11 | 12 | public static InputStream asInputStream(String str) 13 | { 14 | try 15 | { 16 | return new ByteArrayInputStream(str.getBytes(CHARSET)); 17 | } 18 | catch (UnsupportedEncodingException e) 19 | { 20 | // doesn't know UTF8? Why is this even a checked exception? 21 | throw new RuntimeException(e); 22 | } 23 | } 24 | 25 | public static String asString(ByteArrayOutputStream out) 26 | { 27 | try 28 | { 29 | return new String(out.toByteArray(), CHARSET); 30 | } 31 | catch (UnsupportedEncodingException e) 32 | { 33 | // doesn't know UTF8? Why is this even a checked exception? 34 | throw new RuntimeException(e); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/changes/MapDataChangesParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.changes; 2 | 3 | import java.text.ParseException; 4 | 5 | import de.westnordost.osmapi.map.MapDataFactory; 6 | import de.westnordost.osmapi.map.MapDataParser; 7 | 8 | /** Parses a xml in <osmChange> format */ 9 | public class MapDataChangesParser extends MapDataParser 10 | { 11 | private final MapDataChangesHandler handler; 12 | 13 | public MapDataChangesParser(MapDataChangesHandler handler, MapDataFactory factory) 14 | { 15 | super(handler, factory); 16 | this.handler = handler; 17 | } 18 | 19 | @Override 20 | protected void onStartElement() throws ParseException 21 | { 22 | super.onStartElement(); 23 | 24 | String name = getName(); 25 | 26 | if(name.equals("create")) 27 | { 28 | handler.onStartCreations(); 29 | } 30 | else if(name.equals("modify")) 31 | { 32 | handler.onStartModifications(); 33 | } 34 | else if(name.equals("delete")) 35 | { 36 | handler.onStartDeletions(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /libs/map/src/test/java/de/westnordost/osmapi/map/data/OsmRelationMemberTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import org.junit.Test; 4 | 5 | import de.westnordost.osmapi.map.data.Element.Type; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | public class OsmRelationMemberTest 10 | { 11 | private static final String TOO_LONG = 12 | "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy " 13 | + "eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptu" 14 | + "a. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gube" 15 | + "rgren, no sea takimata "; 16 | 17 | @Test public void setTooLongRoleFails() 18 | { 19 | assertThrows( 20 | IllegalArgumentException.class, 21 | () -> new OsmRelationMember(1,"jo", Type.NODE).setRole(TOO_LONG) 22 | ); 23 | } 24 | 25 | @Test public void initWithTooLongRoleFails() 26 | { 27 | assertThrows( 28 | IllegalArgumentException.class, 29 | () -> new OsmRelationMember(1,TOO_LONG, Type.NODE) 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /libs/notes/src/main/java/de/westnordost/osmapi/notes/Note.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.notes; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import de.westnordost.osmapi.map.data.LatLon; 9 | 10 | /** A note from the osm notes API */ 11 | public class Note implements Serializable 12 | { 13 | private static final long serialVersionUID = 2L; 14 | 15 | public LatLon position; 16 | 17 | public long id; 18 | public Instant createdAt; 19 | /** the date the note was closed. May be null if the note is not closed. */ 20 | public Instant closedAt; 21 | public Status status; 22 | public List comments = new ArrayList<>(); 23 | 24 | public boolean isOpen() 25 | { 26 | return status == Status.OPEN; 27 | } 28 | 29 | public boolean isClosed() 30 | { 31 | return status == Status.CLOSED; 32 | } 33 | 34 | public boolean isHidden() 35 | { 36 | return status == Status.HIDDEN; 37 | } 38 | 39 | public enum Status 40 | { 41 | OPEN, 42 | CLOSED, 43 | HIDDEN 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/map/data/LatLons.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | public class LatLons 4 | { 5 | public static LatLon MIN_VALUE = new LatLon() 6 | { 7 | public double getLatitude() { return -90; } 8 | public double getLongitude() { return -180; } 9 | }; 10 | 11 | public static LatLon MAX_VALUE = new LatLon() 12 | { 13 | public double getLatitude() { return +90; } 14 | public double getLongitude() { return +180; } 15 | }; 16 | 17 | public static void checkValidity(double lat, double lon) 18 | { 19 | if(!isValid(lat, lon)) 20 | { 21 | throw new IllegalArgumentException("Latitude " + lat + ", Longitude " + lon + 22 | " is not a valid position."); 23 | } 24 | } 25 | 26 | public static void checkValidity(LatLon x) 27 | { 28 | checkValidity(x.getLatitude(), x.getLongitude()); 29 | } 30 | 31 | public static boolean isValid(LatLon x) 32 | { 33 | return isValid(x.getLatitude(), x.getLongitude()); 34 | } 35 | 36 | public static boolean isValid(double lat, double lon) 37 | { 38 | return lon >= MIN_VALUE.getLongitude() && lon <= MAX_VALUE.getLongitude() 39 | && lat >= MIN_VALUE.getLatitude() && lat <= MAX_VALUE.getLatitude(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/user/PermissionsParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import de.westnordost.osmapi.ApiResponseReader; 9 | import de.westnordost.osmapi.common.XmlParser; 10 | 11 | /** Parses the permissions this osm user has on this server (API 0.6). */ 12 | public class PermissionsParser extends XmlParser implements ApiResponseReader> 13 | { 14 | private static final String PERMISSIONS = "permissions"; 15 | 16 | List permissions; 17 | 18 | public List parse(InputStream in) throws IOException 19 | { 20 | doParse(in); 21 | return permissions; 22 | } 23 | 24 | @Override 25 | protected void onStartElement() 26 | { 27 | if(PERMISSIONS.equals(getName())) 28 | { 29 | permissions = new ArrayList<>(); 30 | } 31 | else if(PERMISSIONS.equals(getParentName())) 32 | { 33 | if("permission".equals(getName())) 34 | { 35 | permissions.add(getAttribute("name")); 36 | } 37 | } 38 | } 39 | 40 | @Override 41 | protected void onEndElement() 42 | { 43 | // nothing... 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /libs/traces/src/main/java/de/westnordost/osmapi/traces/GpsTraceDetails.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | import java.util.List; 6 | 7 | import de.westnordost.osmapi.map.data.LatLon; 8 | 9 | /** Details aka meta informations for a GPS, so not the actual trace */ 10 | public class GpsTraceDetails implements Serializable 11 | { 12 | private static final long serialVersionUID = 2L; 13 | 14 | /** See http://wiki.openstreetmap.org/wiki/Visibility_of_GPS_traces for a more detailed description */ 15 | public enum Visibility 16 | { 17 | PRIVATE, 18 | TRACKABLE, 19 | PUBLIC, 20 | IDENTIFIABLE 21 | } 22 | 23 | public long id; 24 | public String name; 25 | /** null until the whole trace has been imported completely by the server */ 26 | public LatLon position; 27 | /** (current) user name of the uploader */ 28 | public String userName; 29 | public Visibility visibility; 30 | /** whether the server did not complete the import of the trace yet */ 31 | public boolean pending; 32 | public Instant createdAt; 33 | /** may be empty/null */ 34 | public String description; 35 | /** may be empty/null */ 36 | public List tags; 37 | } 38 | -------------------------------------------------------------------------------- /libs/traces/src/main/java/de/westnordost/osmapi/traces/GpsTrackpoint.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | import de.westnordost.osmapi.map.data.LatLon; 7 | 8 | public class GpsTrackpoint implements Serializable 9 | { 10 | private static final long serialVersionUID = 2L; 11 | 12 | public GpsTrackpoint(LatLon position, Instant time) 13 | { 14 | this(position, time, false, null, null); 15 | } 16 | 17 | public GpsTrackpoint( 18 | LatLon position, Instant time, boolean isFirstPointInTrackSegment, 19 | Float horizontalDilutionOfPrecision, Float elevation 20 | ) 21 | { 22 | this.position = position; 23 | this.time = time; 24 | this.isFirstPointInTrackSegment = isFirstPointInTrackSegment; 25 | this.horizontalDilutionOfPrecision = horizontalDilutionOfPrecision; 26 | this.elevation = elevation; 27 | } 28 | 29 | /** whether this trackpoint is the first point in a new segment/track (no differentiation made) */ 30 | public final boolean isFirstPointInTrackSegment; 31 | 32 | public final LatLon position; 33 | public final Instant time; 34 | 35 | public final Float horizontalDilutionOfPrecision; 36 | public final Float elevation; 37 | } 38 | -------------------------------------------------------------------------------- /libs/user/src/main/java/de/westnordost/osmapi/user/UserDetails.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | import de.westnordost.osmapi.map.data.LatLon; 7 | 8 | /** OSM user details. The OSM api does not reveal personal information of other users, so this 9 | * information is only available to the current user. */ 10 | public class UserDetails extends UserInfo implements Serializable 11 | { 12 | private static final long serialVersionUID = 2L; 13 | 14 | public UserDetails(long id, String displayName) 15 | { 16 | super(id, displayName); 17 | } 18 | 19 | public boolean considersHisContributionsAsPublicDomain; 20 | 21 | /** user's self-chosen home zoom level is something between 0-19. Null if not set. */ 22 | public Byte homeZoom; 23 | /** user's self-chosen home location. Null if not set. */ 24 | public LatLon homeLocation; 25 | 26 | /** the language and country codes of the user's preferred languages, sorted by 27 | * preferedness. The format is i.e. "en-US" or "en" (according to ISO 639-1 and ISO 3166) */ 28 | public List preferredLanguages; 29 | 30 | public int inboxMessageCount; 31 | public int unreadMessagesCount; 32 | public int sentMessagesCount; 33 | } 34 | -------------------------------------------------------------------------------- /libs/user/src/main/java/de/westnordost/osmapi/user/PreferencesParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import de.westnordost.osmapi.ApiResponseReader; 9 | import de.westnordost.osmapi.common.XmlParser; 10 | 11 | /** Parses the preferences of this osm user on this server (API 0.6). */ 12 | public class PreferencesParser extends XmlParser implements ApiResponseReader> 13 | { 14 | private static final String PREFERENCES = "preferences"; 15 | 16 | Map preferences; 17 | 18 | @Override 19 | public Map parse(InputStream in) throws IOException 20 | { 21 | doParse(in); 22 | return preferences; 23 | } 24 | 25 | @Override 26 | protected void onStartElement() 27 | { 28 | if(PREFERENCES.equals(getName())) 29 | { 30 | preferences = new HashMap<>(); 31 | } 32 | else if(PREFERENCES.equals(getParentName())) 33 | { 34 | if("preference".equals(getName())) 35 | { 36 | preferences.put(getAttribute("k"), getAttribute("v")); 37 | } 38 | } 39 | } 40 | 41 | @Override 42 | protected void onEndElement() 43 | { 44 | // nothing... 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/OsmWay.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import de.westnordost.osmapi.changesets.Changeset; 9 | 10 | public class OsmWay extends OsmElement implements Way, Serializable 11 | { 12 | private static final long serialVersionUID = 2L; 13 | 14 | private final ModificationAwareList nodes; 15 | 16 | public OsmWay(long id, int version, List nodes, 17 | Map tags, Changeset changeset, Instant editedAt) 18 | { 19 | super(id, version, tags, changeset, editedAt); 20 | this.nodes = new ModificationAwareList<>(nodes); 21 | } 22 | 23 | public OsmWay(long id, int version, List nodes, Map tags) 24 | { 25 | this(id, version, nodes, tags, null, null); 26 | } 27 | 28 | @Override 29 | public boolean isModified() 30 | { 31 | return nodes.isModified() || super.isModified(); 32 | } 33 | 34 | public boolean isClosed() 35 | { 36 | return nodes.size() >= 3 && nodes.get(0).equals(nodes.get(nodes.size() - 1)); 37 | } 38 | 39 | @Override 40 | public List getNodeIds() 41 | { 42 | return nodes; 43 | } 44 | 45 | @Override 46 | public Type getType() 47 | { 48 | return Type.WAY; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/OsmMapDataFactory.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map; 2 | 3 | import de.westnordost.osmapi.changesets.Changeset; 4 | import de.westnordost.osmapi.map.data.Element.Type; 5 | import de.westnordost.osmapi.map.data.*; 6 | 7 | import java.time.Instant; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class OsmMapDataFactory implements MapDataFactory 12 | { 13 | @Override 14 | public Node createNode(long id, int version, Double lat, Double lon, Map tags, 15 | Changeset changeset, Instant editedAt) 16 | { 17 | return new OsmNode(id, version, lat, lon, tags, changeset, editedAt); 18 | } 19 | 20 | @Override 21 | public Way createWay(long id, int version, List nodes, Map tags, 22 | Changeset changeset, Instant editedAt) 23 | { 24 | return new OsmWay(id, version, nodes, tags, changeset, editedAt); 25 | } 26 | 27 | @Override 28 | public Relation createRelation(long id, int version, List members, 29 | Map tags, Changeset changeset, Instant editedAt) 30 | { 31 | return new OsmRelation(id, version, members, tags, changeset, editedAt); 32 | } 33 | 34 | @Override 35 | public RelationMember createRelationMember(long ref, String role, Type type) 36 | { 37 | return new OsmRelationMember( ref, role, type ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/OsmRelation.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import de.westnordost.osmapi.changesets.Changeset; 9 | 10 | public class OsmRelation extends OsmElement implements Relation, Serializable 11 | { 12 | private static final long serialVersionUID = 2L; 13 | 14 | private final ModificationAwareList members; 15 | 16 | public OsmRelation(long id, int version, List members, 17 | Map tags, Changeset changeset, Instant editedAt) 18 | { 19 | super(id, version, tags, changeset, editedAt); 20 | this.members = new ModificationAwareList<>(members); 21 | } 22 | 23 | public OsmRelation(long id, int version, List members, Map tags) 24 | { 25 | this(id, version, members, tags, null, null); 26 | } 27 | 28 | @Override 29 | public List getMembers() 30 | { 31 | return members; 32 | } 33 | 34 | @Override 35 | public Type getType() 36 | { 37 | return Type.RELATION; 38 | } 39 | 40 | @Override 41 | public boolean isModified() 42 | { 43 | if(members.isModified()) return true; 44 | 45 | for(RelationMember member : members) 46 | { 47 | if(member.isModified()) 48 | { 49 | return true; 50 | } 51 | } 52 | return super.isModified(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/handler/OneElementTypeHandler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.handler; 2 | 3 | import de.westnordost.osmapi.map.data.BoundingBox; 4 | import de.westnordost.osmapi.map.data.Node; 5 | import de.westnordost.osmapi.map.data.Relation; 6 | import de.westnordost.osmapi.map.data.Way; 7 | 8 | /** Handles one OsmElement type (Node, Way, Relation, Bounds) and ignores everything else */ 9 | public abstract class OneElementTypeHandler implements MapDataHandler 10 | { 11 | private final Class tClass; 12 | 13 | public OneElementTypeHandler(Class tClass) 14 | { 15 | this.tClass = tClass; 16 | } 17 | 18 | @SuppressWarnings("unchecked") 19 | @Override 20 | public void handle(BoundingBox bounds) 21 | { 22 | if(tClass.isAssignableFrom(bounds.getClass())) handleElement((T) bounds); 23 | } 24 | 25 | @SuppressWarnings("unchecked") 26 | @Override 27 | public void handle(Node node) 28 | { 29 | if(tClass.isAssignableFrom(node.getClass())) handleElement((T) node); 30 | } 31 | 32 | @SuppressWarnings("unchecked") 33 | @Override 34 | public void handle(Way way) 35 | { 36 | if(tClass.isAssignableFrom(way.getClass())) handleElement((T) way); 37 | } 38 | 39 | @SuppressWarnings("unchecked") 40 | @Override 41 | public void handle(Relation relation) 42 | { 43 | if(tClass.isAssignableFrom(relation.getClass())) handleElement((T) relation); 44 | } 45 | 46 | protected abstract void handleElement(T element); 47 | } -------------------------------------------------------------------------------- /libs/traces/src/main/java/de/westnordost/osmapi/traces/GpsTraceWriter.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | import java.util.Locale; 6 | 7 | import de.westnordost.osmapi.common.XmlWriter; 8 | 9 | /** Writes only those GPS trace properties that can be changed in an update call to the API. 10 | * It does not write everything, like i.e. the creation date. */ 11 | public class GpsTraceWriter extends XmlWriter 12 | { 13 | private final long id; 14 | private final GpsTraceDetails.Visibility visibility; 15 | private final String description; 16 | private final List tags; 17 | 18 | public GpsTraceWriter(long id, GpsTraceDetails.Visibility visibility, String description, List tags) 19 | { 20 | this.id = id; 21 | this.visibility = visibility; 22 | this.description = description; 23 | this.tags = tags; 24 | } 25 | 26 | @Override 27 | protected void write() throws IOException 28 | { 29 | begin("osm"); 30 | begin("gpx_file"); 31 | writeTrace(); 32 | end(); 33 | end(); 34 | } 35 | 36 | private void writeTrace() throws IOException 37 | { 38 | attribute("id", id); 39 | attribute("visibility", visibility.toString().toLowerCase(Locale.UK)); 40 | 41 | begin("description"); 42 | if(description != null) text(description); 43 | end(); 44 | 45 | if(tags != null) 46 | { 47 | for(String tag : tags) 48 | { 49 | begin("tag"); 50 | text(tag); 51 | end(); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmApiException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** 4 | * Thrown when the OSM Api returns a HTTP error. As opposed to the OsmConnectionException, the type 5 | * of the thrown error is in direct relation to the user's request, i.e. the request itself was 6 | * invalid, the user was not authenticated or the user was working with old/invalid data. 7 | * 8 | * Any HTTP error codes starting with 4xx will be OsmApiExceptions. 9 | * 10 | * See subclasses. 11 | */ 12 | public class OsmApiException extends RuntimeException 13 | { 14 | private static final long serialVersionUID = 1L; 15 | 16 | private int errorCode; 17 | private String errorTitle; 18 | private String description; 19 | 20 | public OsmApiException(Throwable cause) 21 | { 22 | super(cause); 23 | } 24 | 25 | public OsmApiException(int errorCode, String errorTitle, String description) 26 | { 27 | this.errorCode = errorCode; 28 | this.errorTitle = errorTitle; 29 | this.description = description; 30 | } 31 | 32 | public int getErrorCode() 33 | { 34 | return errorCode; 35 | } 36 | 37 | public String getErrorTitle() 38 | { 39 | return errorTitle; 40 | } 41 | 42 | public String getDescription() 43 | { 44 | return description; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | if( getCause() != null ) return super.toString(); 50 | 51 | String name = getClass().getName(); 52 | return name + ": " + errorTitle + " ("+errorCode+") - " + description; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/changes/MapDataDiffParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.changes; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.Locale; 6 | 7 | import de.westnordost.osmapi.ApiResponseReader; 8 | import de.westnordost.osmapi.common.Handler; 9 | import de.westnordost.osmapi.common.XmlParser; 10 | import de.westnordost.osmapi.map.data.Element; 11 | 12 | /** Parses a <diffResult> sent by the server when uploading a changeset */ 13 | public class MapDataDiffParser extends XmlParser implements ApiResponseReader 14 | { 15 | private static final String NODE = "node", 16 | WAY = "way", 17 | RELATION = "relation"; 18 | 19 | private final Handler handler; 20 | 21 | public MapDataDiffParser(Handler handler) 22 | { 23 | this.handler = handler; 24 | } 25 | 26 | @Override 27 | public Void parse(InputStream in) throws IOException 28 | { 29 | doParse(in); 30 | return null; 31 | } 32 | 33 | @Override 34 | protected void onStartElement() 35 | { 36 | String name = getName(); 37 | 38 | if (name.equals(NODE) || name.equals(WAY) || name.equals(RELATION)) 39 | { 40 | DiffElement e = new DiffElement(); 41 | e.type = Element.Type.valueOf(name.toUpperCase(Locale.UK)); 42 | e.clientId = getLongAttribute("old_id"); 43 | e.serverId = getLongAttribute("new_id"); 44 | e.serverVersion = getIntAttribute("new_version"); 45 | handler.handle(e); 46 | } 47 | } 48 | 49 | @Override 50 | protected void onEndElement() 51 | { 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /libs/user/src/test/java/de/westnordost/osmapi/user/UserApiTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.util.Arrays; 7 | 8 | import de.westnordost.osmapi.ConnectionTestFactory; 9 | import de.westnordost.osmapi.common.errors.OsmAuthorizationException; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | public class UserApiTest 14 | { 15 | private UserApi privilegedApi; 16 | private UserApi anonymousApi; 17 | private UserApi unprivilegedApi; 18 | 19 | @Before public void setUp() 20 | { 21 | anonymousApi = new UserApi(ConnectionTestFactory.createConnection(null)); 22 | privilegedApi = new UserApi(ConnectionTestFactory.createConnection( 23 | ConnectionTestFactory.User.ALLOW_EVERYTHING)); 24 | unprivilegedApi = new UserApi(ConnectionTestFactory.createConnection( 25 | ConnectionTestFactory.User.ALLOW_NOTHING)); 26 | } 27 | 28 | @Test public void getUserDetailsUnprivilegedFails() 29 | { 30 | assertThrows(OsmAuthorizationException.class, () -> unprivilegedApi.getMine()); 31 | } 32 | 33 | @Test public void getUserDetailsPrivilegedWorks() 34 | { 35 | // should just not throw any exceptions. Since the user details may change, we do not 36 | // check the validity of the data here 37 | assertNotNull(privilegedApi.getMine()); 38 | } 39 | 40 | @Test public void getUserInfo() 41 | { 42 | assertNull(unprivilegedApi.get(0L)); 43 | assertNotNull(unprivilegedApi.get(1L)); 44 | assertNotNull(anonymousApi.get(1L)); 45 | } 46 | 47 | @Test public void getUserInfos() 48 | { 49 | assertEquals(2,unprivilegedApi.getAll(Arrays.asList(1L, 2L)).size()); 50 | assertEquals(2,anonymousApi.getAll(Arrays.asList(1L, 2L)).size()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/OsmNode.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | import java.util.Map; 6 | 7 | import de.westnordost.osmapi.changesets.Changeset; 8 | 9 | public class OsmNode extends OsmElement implements Node, Serializable 10 | { 11 | private static final long serialVersionUID = 2L; 12 | 13 | private boolean modified; 14 | private LatLon pos; 15 | 16 | public OsmNode(long id, int version, Double lat, Double lon, 17 | Map tags, Changeset changeset, Instant editedAt) 18 | { 19 | super(id, version, tags, changeset, editedAt); 20 | if (lat != null && lon != null) { 21 | this.pos = new OsmLatLon(lat, lon); 22 | } 23 | } 24 | 25 | public OsmNode(long id, int version, LatLon pos, 26 | Map tags, Changeset changeset, Instant editedAt) 27 | { 28 | super(id, version, tags, changeset, editedAt); 29 | this.pos = pos; 30 | } 31 | 32 | // convenience constructors 33 | 34 | public OsmNode(long id, int version, Double lat, Double lon, Map tags) 35 | { 36 | this(id, version, lat, lon, tags, null, null); 37 | } 38 | 39 | public OsmNode(long id, int version, LatLon pos, Map tags) 40 | { 41 | this(id, version, pos, tags, null, null); 42 | } 43 | 44 | @Override 45 | public LatLon getPosition() 46 | { 47 | return pos; 48 | } 49 | 50 | public void setPosition(LatLon pos) 51 | { 52 | this.pos = pos; 53 | modified = true; 54 | } 55 | 56 | @Override 57 | public boolean isModified() 58 | { 59 | return modified || super.isModified(); 60 | } 61 | 62 | @Override 63 | public Type getType() 64 | { 65 | return Type.NODE; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /libs/core/src/test/java/de/westnordost/osmapi/OsmApiErrorFactoryTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi; 2 | 3 | import org.junit.Test; 4 | 5 | import java.net.HttpURLConnection; 6 | 7 | import de.westnordost.osmapi.common.errors.OsmApiException; 8 | import de.westnordost.osmapi.common.errors.OsmAuthorizationException; 9 | import de.westnordost.osmapi.common.errors.OsmBadUserInputException; 10 | import de.westnordost.osmapi.common.errors.OsmConflictException; 11 | import de.westnordost.osmapi.common.errors.OsmConnectionException; 12 | import de.westnordost.osmapi.common.errors.OsmNotFoundException; 13 | import de.westnordost.osmapi.common.errors.OsmServiceUnavailableException; 14 | 15 | import static org.junit.Assert.*; 16 | 17 | public class OsmApiErrorFactoryTest 18 | { 19 | 20 | @Test public void error() 21 | { 22 | for(int i = 0; i < 600; ++i) 23 | { 24 | RuntimeException e = OsmApiErrorFactory.createError(i, "test", "description"); 25 | 26 | if(i >= 400 && i < 500) assertTrue(e instanceof OsmApiException); 27 | else assertTrue(e instanceof OsmConnectionException); 28 | 29 | switch(i) 30 | { 31 | case HttpURLConnection.HTTP_UNAVAILABLE: 32 | assertTrue(e instanceof OsmServiceUnavailableException); 33 | break; 34 | case HttpURLConnection.HTTP_NOT_FOUND: 35 | case HttpURLConnection.HTTP_GONE: 36 | assertTrue(e instanceof OsmNotFoundException); 37 | break; 38 | case HttpURLConnection.HTTP_FORBIDDEN: 39 | assertTrue(e instanceof OsmAuthorizationException); 40 | break; 41 | case HttpURLConnection.HTTP_CONFLICT: 42 | assertTrue(e instanceof OsmConflictException); 43 | break; 44 | case HttpURLConnection.HTTP_BAD_REQUEST: 45 | assertTrue(e instanceof OsmBadUserInputException); 46 | break; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /libs/traces/src/test/java/de/westnordost/osmapi/traces/GpsTracesWriterTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import de.westnordost.osmapi.TestUtils; 11 | import de.westnordost.osmapi.common.SingleElementHandler; 12 | 13 | import static org.junit.Assert.assertEquals; 14 | 15 | public class GpsTracesWriterTest 16 | { 17 | @Test public void writeAll() throws IOException 18 | { 19 | List tags = new ArrayList<>(); 20 | tags.add("abc"); 21 | tags.add("def"); 22 | 23 | writeAndReadTest(123, GpsTraceDetails.Visibility.TRACKABLE, "xyz", tags); 24 | } 25 | 26 | @Test public void writeMin() throws IOException 27 | { 28 | writeAndReadTest(456, GpsTraceDetails.Visibility.IDENTIFIABLE, null, null); 29 | } 30 | 31 | private GpsTraceDetails writeAndRead(long id, GpsTraceDetails.Visibility visibility, String description, List tags) 32 | throws IOException 33 | { 34 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 35 | new GpsTraceWriter(id, visibility, description, tags).write(out); 36 | String xml = TestUtils.asString(out); 37 | SingleElementHandler handler = new SingleElementHandler<>(); 38 | new GpsTracesParser(handler).parse(TestUtils.asInputStream(xml)); 39 | return handler.get(); 40 | } 41 | 42 | private void writeAndReadTest(long id, GpsTraceDetails.Visibility visibility, String description, List tags) 43 | throws IOException 44 | { 45 | GpsTraceDetails trace = writeAndRead(id, visibility, description, tags); 46 | assertEquals(id, trace.id); 47 | assertEquals(visibility, trace.visibility); 48 | assertEquals(description, trace.description); 49 | assertEquals(tags, trace.tags); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /libs/traces/src/test/resources/track.gpx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test Track 5 | osmtracker, test 6 | 7 | 8 | 1029.89940680377 9 | 10 | 11 | 0 12 | 6.0 13 | 14 | 204.029998779297 15 | 2.0 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 1015.13159253262 24 | 25 | 8.908860206604 26 | 27 | 0.361938893795013 28 | 204.690002441406 29 | 2.0 30 | 31 | 32 | 33 | 1007.3209409276 34 | 35 | 13.0176601409912 36 | 37 | 0.271533757448196 38 | 204.360000610352 39 | 2.0 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /libs/core/src/test/java/de/westnordost/osmapi/OsmConnectionTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi; 2 | 3 | import org.junit.Test; 4 | 5 | import de.westnordost.osmapi.ConnectionTestFactory.User; 6 | import de.westnordost.osmapi.common.errors.OsmApiReadResponseException; 7 | import de.westnordost.osmapi.common.errors.OsmAuthorizationException; 8 | import de.westnordost.osmapi.common.errors.OsmConnectionException; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | public class OsmConnectionTest 13 | { 14 | @Test public void authorizationException() 15 | { 16 | assertThrows( 17 | OsmAuthorizationException.class, 18 | () -> { 19 | OsmConnection osm = ConnectionTestFactory.createConnection(null); 20 | osm.makeAuthenticatedRequest("changeset/create", "PUT", null, null); 21 | } 22 | ); 23 | } 24 | 25 | @Test public void authorizationException2() 26 | { 27 | assertThrows( 28 | OsmAuthorizationException.class, 29 | () -> { 30 | OsmConnection osm = ConnectionTestFactory.createConnection(User.UNKNOWN); 31 | osm.makeAuthenticatedRequest("changeset/create", "PUT", null, null); 32 | } 33 | ); 34 | } 35 | 36 | @Test public void connectionException() 37 | { 38 | assertThrows( 39 | OsmConnectionException.class, 40 | () -> { 41 | OsmConnection osm = new OsmConnection("http://cant.connect.to.this.server.hm", "blub", null); 42 | osm.makeRequest("doesntMatter", null); 43 | } 44 | ); 45 | } 46 | 47 | @Test public void errorParsingApiResponse() 48 | { 49 | assertThrows( 50 | OsmApiReadResponseException.class, 51 | () -> { 52 | OsmConnection osm = ConnectionTestFactory.createConnection(null); 53 | osm.makeRequest("capabilities", (ApiResponseReader) in -> { 54 | throw new Exception(); 55 | }); 56 | } 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/errors/OsmConnectionException.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common.errors; 2 | 3 | /** (A proper) connection to the server cannot be established for some reason. This error usually 4 | * wraps an IOException when trying to reach the server but also includes any error that inhibits 5 | * the user to talk with the OSM Api for reasons that have no relation to the user's request (i.e. 6 | * whether it was valid or not). So in any case, nothing the user can do anything about. 7 | * 8 | * For example, if the Api correctly replies to the HTTP request but it replies that the service is 9 | * unavailable, it is also a OsmConnectionException (see OsmServiceUnavailableException). 10 | * Or, more generally, any HTTP error codes starting with 5xx will be OsmConnectionExceptions. 11 | * 12 | * See subclasses for further possibilities. 13 | * */ 14 | public class OsmConnectionException extends RuntimeException 15 | { 16 | private static final long serialVersionUID = 1L; 17 | 18 | private int errorCode; 19 | private String errorTitle; 20 | private String description; 21 | 22 | public OsmConnectionException(Throwable cause) 23 | { 24 | super(cause); 25 | } 26 | 27 | public OsmConnectionException(int errorCode, String errorTitle, String description) 28 | { 29 | this.errorCode = errorCode; 30 | this.errorTitle = errorTitle; 31 | this.description = description; 32 | } 33 | 34 | public int getErrorCode() 35 | { 36 | return errorCode; 37 | } 38 | 39 | public String getErrorTitle() 40 | { 41 | return errorTitle; 42 | } 43 | 44 | public String getDescription() 45 | { 46 | return description; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | if( getCause() != null ) return super.toString(); 52 | 53 | String name = getClass().getName(); 54 | return name + ": " + errorTitle + " ("+errorCode+") - " + description; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/map/data/Fixed1E7LatLon.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * A geo position (without height) implemented using two fixed 1E7 integers, meaning that the 7 | * maximum precision is limited to 7 decimal points. This is exactly the maximum precision the 8 | * positions are saved in the OSM database. 9 | * 10 | * This saves 8 byte per coordinate from an implementation based on doubles, but every access 11 | * involves a division operation. If the goal is to save disk space, rather just serialize a latlon 12 | * with two integers instead. This implementation should only be chosen if the goal is to save RAM 13 | * space but CPU is not the bottleneck. 14 | */ 15 | public class Fixed1E7LatLon implements LatLon, Serializable 16 | { 17 | private static final long serialVersionUID = 1L; 18 | 19 | private final int latitude; 20 | private final int longitude; 21 | 22 | /** @throws IllegalArgumentException if the given latitude and longitude do not make up a valid 23 | * position*/ 24 | public Fixed1E7LatLon(double latitude, double longitude) 25 | { 26 | LatLons.checkValidity(latitude, longitude); 27 | 28 | this.latitude = Fixed1E7.doubleToFixed(latitude); 29 | this.longitude = Fixed1E7.doubleToFixed(longitude); 30 | } 31 | 32 | @Override 33 | public double getLatitude() 34 | { 35 | return Fixed1E7.toDouble(latitude); 36 | } 37 | 38 | @Override 39 | public double getLongitude() 40 | { 41 | return Fixed1E7.toDouble(longitude); 42 | } 43 | 44 | @Override 45 | public boolean equals(Object obj) 46 | { 47 | if(obj == this) return true; 48 | if(obj == null || !(obj instanceof LatLon)) return false; 49 | LatLon other = (LatLon) obj; 50 | return other.getLatitude() == getLatitude() && other.getLongitude() == getLongitude(); 51 | } 52 | 53 | @Override 54 | public int hashCode() 55 | { 56 | return latitude * 31 + longitude; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /libs/user/src/main/java/de/westnordost/osmapi/user/UserInfo.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | import java.util.List; 6 | 7 | /** Non-private info of a user queried through the user-API */ 8 | public class UserInfo extends User implements Serializable 9 | { 10 | private static final long serialVersionUID = 3L; 11 | 12 | public UserInfo(long id, String displayName) 13 | { 14 | super(id, displayName); 15 | } 16 | 17 | public Instant createdAt; 18 | 19 | /** aka the number of edits */ 20 | public int changesetsCount; 21 | public int gpsTracesCount; 22 | 23 | /** URL to the user's profile picture. May be null if no profile picture has been chosen. */ 24 | public String profileImageUrl; 25 | /** The profile description is formatted with markdown. May be null if no description was 26 | * provided. */ 27 | public String profileDescription; 28 | 29 | public boolean hasAgreedToContributorTerms; 30 | 31 | /** may be null if the user has no roles */ 32 | public List roles; 33 | 34 | /** whether the user is currently blocked (=cannot make any modifications on the map). */ 35 | public boolean isBlocked; 36 | 37 | /** The company the user is associated with or null if none */ 38 | public String company; 39 | 40 | /** The social network(s) the user is reachable over. May be empty or null. */ 41 | public List socialLinks; 42 | 43 | /** whether this user holds the given role. See UserRole for constants for known roles 44 | * and the methods isModerator and isAdministrator */ 45 | public boolean hasRole(String roleName) 46 | { 47 | return roles != null && roles.contains(roleName); 48 | } 49 | 50 | public boolean isModerator() 51 | { 52 | return hasRole(UserRole.MODERATOR); 53 | } 54 | 55 | public boolean isAdministrator() 56 | { 57 | return hasRole(UserRole.ADMINISTRATOR); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/map/data/OsmLatLon.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * A geo position (without height). 7 | * 8 | * In prior versions, it was based on two integers, saving 8 byte from an implementation based on 9 | * doubles, but every access thus involved a division operation so that was bullocks for general 10 | * usage. 11 | */ 12 | public class OsmLatLon implements LatLon, Serializable 13 | { 14 | private static final long serialVersionUID = 2L; 15 | 16 | private final double latitude; 17 | private final double longitude; 18 | 19 | /** @throws IllegalArgumentException if the given latitude and longitude do not make up a valid 20 | * position*/ 21 | public OsmLatLon(double latitude, double longitude) 22 | { 23 | LatLons.checkValidity(latitude, longitude); 24 | 25 | this.latitude = latitude; 26 | this.longitude = longitude; 27 | } 28 | 29 | public OsmLatLon(LatLon other) 30 | { 31 | this.latitude = other.getLatitude(); 32 | this.longitude = other.getLongitude(); 33 | } 34 | 35 | public static OsmLatLon parseLatLon(String lat, String lon) 36 | { 37 | return new OsmLatLon(Double.parseDouble(lat), Double.parseDouble(lon)); 38 | } 39 | 40 | @Override 41 | public double getLatitude() 42 | { 43 | return latitude; 44 | } 45 | 46 | @Override 47 | public double getLongitude() 48 | { 49 | return longitude; 50 | } 51 | 52 | @Override 53 | public boolean equals(Object obj) 54 | { 55 | if(obj == this) return true; 56 | if(obj == null || !(obj instanceof LatLon)) return false; 57 | LatLon other = (LatLon) obj; 58 | return other.getLatitude() == getLatitude() && other.getLongitude() == getLongitude(); 59 | } 60 | 61 | @Override public int hashCode() 62 | { 63 | return 31 * hashCode(latitude) + hashCode(longitude); 64 | } 65 | 66 | private static int hashCode(double val) 67 | { 68 | long longBits = Double.doubleToLongBits(val); 69 | return (int) (longBits ^ (longBits >>> 32)); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /libs/map/src/test/java/de/westnordost/osmapi/map/changes/MapDataDiffParserTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.changes; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.IOException; 6 | 7 | import de.westnordost.osmapi.TestUtils; 8 | import de.westnordost.osmapi.common.SingleElementHandler; 9 | import de.westnordost.osmapi.map.data.Element; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | public class MapDataDiffParserTest 14 | { 15 | @Test public void empty() 16 | { 17 | DiffElement e = parseOne(""); 18 | 19 | assertNull(e); 20 | } 21 | 22 | @Test public void fields() 23 | { 24 | DiffElement e = parseOne(""); 25 | 26 | assertEquals(1, e.clientId); 27 | assertEquals(2, (long) e.serverId); 28 | assertEquals(3, (int) e.serverVersion); 29 | } 30 | 31 | @Test public void nullableFields() 32 | { 33 | DiffElement e = parseOne(""); 34 | 35 | assertEquals(1, e.clientId); 36 | assertNull(e.serverId); 37 | assertNull(e.serverVersion); 38 | } 39 | 40 | @Test public void node() 41 | { 42 | DiffElement e = parseOne(""); 43 | assertEquals(Element.Type.NODE, e.type); 44 | } 45 | 46 | @Test public void way() 47 | { 48 | DiffElement e = parseOne(""); 49 | assertEquals(Element.Type.WAY, e.type); 50 | } 51 | 52 | @Test public void relation() 53 | { 54 | DiffElement e = parseOne(""); 55 | assertEquals(Element.Type.RELATION, e.type); 56 | } 57 | 58 | private DiffElement parseOne(String xml) 59 | { 60 | try 61 | { 62 | SingleElementHandler handler = new SingleElementHandler<>(); 63 | new MapDataDiffParser(handler).parse(TestUtils.asInputStream(xml)); 64 | return handler.get(); 65 | } 66 | catch(IOException e) 67 | { 68 | throw new RuntimeException(e); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/OsmRelationMember.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.io.Serializable; 4 | 5 | public class OsmRelationMember implements RelationMember, Serializable 6 | { 7 | private static final long serialVersionUID = 1L; 8 | 9 | private boolean modified; 10 | 11 | private final long ref; 12 | private final Element.Type type; 13 | 14 | private String role; 15 | 16 | public OsmRelationMember(long ref, String role, Element.Type type) 17 | { 18 | checkRoleLength(role); 19 | this.ref = ref; 20 | this.role = role; 21 | this.type = type; 22 | } 23 | 24 | public long getRef() 25 | { 26 | return ref; 27 | } 28 | 29 | public String getRole() 30 | { 31 | return role; 32 | } 33 | 34 | public Element.Type getType() 35 | { 36 | return type; 37 | } 38 | 39 | public void setRole(String newRole) 40 | { 41 | checkRoleLength(newRole); 42 | 43 | if(!role.equals(newRole)) 44 | { 45 | modified = true; 46 | role = newRole; 47 | } 48 | } 49 | 50 | private void checkRoleLength(String r) 51 | { 52 | if(r.length() >= 256) 53 | { 54 | throw new IllegalArgumentException("Role \"" + r + "\": Role length is limited" + 55 | "to less than 256 characters."); 56 | } 57 | } 58 | 59 | @Override 60 | public boolean isModified() 61 | { 62 | return modified; 63 | } 64 | 65 | @Override 66 | public boolean equals(Object other) 67 | { 68 | if(other == this) return true; 69 | if(other == null || !(other instanceof RelationMember)) return false; 70 | 71 | RelationMember otherMember = (RelationMember) other; 72 | return 73 | getRole().equals(otherMember.getRole()) && 74 | getRef() == otherMember.getRef() && 75 | getType() == otherMember.getType(); 76 | } 77 | 78 | @Override 79 | public int hashCode() 80 | { 81 | int result = 11; 82 | result = 31 * result + role.hashCode(); 83 | result = 31 * result + type.ordinal(); 84 | result = 31 * result + (int) (ref ^ (ref >>> 32)); 85 | return result; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /libs/map/src/test/java/de/westnordost/osmapi/map/changes/MapDataChangesParserTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.changes; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.IOException; 6 | 7 | import de.westnordost.osmapi.TestUtils; 8 | import de.westnordost.osmapi.map.OsmMapDataFactory; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | public class MapDataChangesParserTest 13 | { 14 | @Test public void emptyChange() 15 | { 16 | MapDataChanges changes = parse(""); 17 | 18 | assertTrue(changes.getAll().isEmpty()); 19 | } 20 | 21 | @Test public void deletions() 22 | { 23 | MapDataChanges changes = 24 | parse(""); 25 | 26 | assertEquals(1, changes.getDeletions().size()); 27 | assertEquals(0, changes.getCreations().size()); 28 | assertEquals(0, changes.getModifications().size()); 29 | } 30 | 31 | @Test public void creations() 32 | { 33 | MapDataChanges changes = 34 | parse(""); 35 | 36 | assertEquals(0, changes.getDeletions().size()); 37 | assertEquals(1, changes.getCreations().size()); 38 | assertEquals(0, changes.getModifications().size()); 39 | } 40 | 41 | @Test public void modifications() 42 | { 43 | MapDataChanges changes = 44 | parse(""); 45 | 46 | assertEquals(0, changes.getDeletions().size()); 47 | assertEquals(0, changes.getCreations().size()); 48 | assertEquals(1, changes.getModifications().size()); 49 | } 50 | 51 | private MapDataChanges parse(String xml) 52 | { 53 | try 54 | { 55 | SimpleMapDataChangesHandler changeMapDataHandler = new SimpleMapDataChangesHandler(); 56 | new MapDataChangesParser(changeMapDataHandler, new OsmMapDataFactory()).parse( 57 | TestUtils.asInputStream(xml)); 58 | return changeMapDataHandler; 59 | } 60 | catch(IOException e) 61 | { 62 | throw new RuntimeException(e); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /libs/core/src/test/java/de/westnordost/osmapi/user/PermissionsApiTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.List; 6 | 7 | import de.westnordost.osmapi.ConnectionTestFactory; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | public class PermissionsApiTest 12 | { 13 | @Test public void getPermissions() 14 | { 15 | PermissionsApi privilegedApi = new PermissionsApi(ConnectionTestFactory.createConnection( 16 | ConnectionTestFactory.User.ALLOW_EVERYTHING)); 17 | PermissionsApi unprivilegedApi = new PermissionsApi(ConnectionTestFactory.createConnection( 18 | ConnectionTestFactory.User.ALLOW_NOTHING)); 19 | 20 | List unprivilegedPermissions = unprivilegedApi.get(); 21 | assertFalse(unprivilegedPermissions.contains(Permission.CHANGE_PREFERENCES)); 22 | assertFalse(unprivilegedPermissions.contains(Permission.MODIFY_MAP)); 23 | // the unprivileged api has this one permission because an authorized App cannot have no 24 | // permissions at all (because then it is not authorized, see?) 25 | //assertFalse(unprivilegedPermissions.contains(Permission.READ_PREFERENCES_AND_USER_DETAILS)); 26 | assertFalse(unprivilegedPermissions.contains(Permission.READ_GPS_TRACES)); 27 | assertFalse(unprivilegedPermissions.contains(Permission.WRITE_GPS_TRACES)); 28 | assertFalse(unprivilegedPermissions.contains(Permission.WRITE_DIARY)); 29 | assertFalse(unprivilegedPermissions.contains(Permission.WRITE_NOTES)); 30 | 31 | List privilegedPermissions = privilegedApi.get(); 32 | assertTrue(privilegedPermissions.contains(Permission.CHANGE_PREFERENCES)); 33 | assertTrue(privilegedPermissions.contains(Permission.MODIFY_MAP)); 34 | assertTrue(privilegedPermissions.contains(Permission.READ_PREFERENCES_AND_USER_DETAILS)); 35 | assertTrue(privilegedPermissions.contains(Permission.READ_GPS_TRACES)); 36 | assertTrue(privilegedPermissions.contains(Permission.WRITE_GPS_TRACES)); 37 | assertTrue(privilegedPermissions.contains(Permission.WRITE_DIARY)); 38 | assertTrue(privilegedPermissions.contains(Permission.WRITE_NOTES)); 39 | assertTrue(privilegedPermissions.contains(Permission.CONSUME_MESSAGES)); 40 | assertTrue(privilegedPermissions.contains(Permission.SEND_MESSAGES)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/OsmApiErrorFactory.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi; 2 | 3 | import java.net.HttpURLConnection; 4 | 5 | import de.westnordost.osmapi.common.errors.*; 6 | 7 | /** Static factory that creates the OsmApiExceptions from the HTTP response code and message */ 8 | public class OsmApiErrorFactory 9 | { 10 | public static RuntimeException createError(int error, String response, String description) 11 | { 12 | switch (error) 13 | { 14 | // 5xx error codes... 15 | case HttpURLConnection.HTTP_UNAVAILABLE: 16 | return new OsmServiceUnavailableException(error, response, description); 17 | 18 | // 4xx error codes... 19 | case HttpURLConnection.HTTP_NOT_FOUND: 20 | case HttpURLConnection.HTTP_GONE: 21 | return new OsmNotFoundException(error, response, description); 22 | case HttpURLConnection.HTTP_FORBIDDEN: 23 | case HttpURLConnection.HTTP_UNAUTHORIZED: 24 | /* 401 unauthorized is returned if the user is not authenticated at all 25 | * 403 forbidden is returned if the user is authenticated, but does not have the 26 | * permission to do the action*/ 27 | return new OsmAuthorizationException(error, response, description); 28 | case HttpURLConnection.HTTP_PRECON_FAILED: 29 | return new OsmPreconditionFailedException(error, response, description); 30 | case HttpURLConnection.HTTP_CONFLICT: 31 | return new OsmConflictException(error, response, description); 32 | case HttpURLConnection.HTTP_BAD_REQUEST: 33 | return new OsmBadUserInputException(error, response, description); 34 | case 429: 35 | return new OsmTooManyRequestsException(error, response, description); 36 | 37 | default: 38 | return createGenericError(error, response, description); 39 | } 40 | } 41 | 42 | private static RuntimeException createGenericError(int error, String response, String description) 43 | { 44 | if(error >= 400 && error < 500 ) 45 | { 46 | // "The 4xx class of status code is intended for situations in which the client seems to have erred" 47 | return new OsmApiException(error, response, description); 48 | } 49 | // "The server failed to fulfill an apparently valid request" 50 | return new OsmConnectionException(error, response, description); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /testutils/src/main/java/de/westnordost/osmapi/ConnectionTestFactory.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi; 2 | 3 | public class ConnectionTestFactory 4 | { 5 | // to request new token: 6 | // 1. open in browser https://master.apis.dev.openstreetmap.org/oauth2/authorize?response_type=code&client_id=_RN0elf1uUxGvpdRQram0s_hfPpOkimHVXhlHN5Cx5I&redirect_uri=https://127.0.0.1/oauth&scope=read_prefs%20write_prefs%20write_diary%20write_api%20read_gpx%20write_gpx%20write_notes%20write_redactions%20consume_messages%20send_messages%20openid 7 | // 2. get the CODE from the url 8 | // 3. POST 'https://master.apis.dev.openstreetmap.org/oauth2/token?grant_type=authorization_code&code=&client_id=_RN0elf1uUxGvpdRQram0s_hfPpOkimHVXhlHN5Cx5I&redirect_uri=https://127.0.0.1/oauth' 9 | private static final String ALLOW_EVERYTHING_TOKEN = "toivxF3JtN5d9CmOW9-r6JbaLwtfDFeJOjaJ4kf5tP4"; 10 | 11 | // to request new token: 12 | // 1. open in browser https://master.apis.dev.openstreetmap.org/oauth2/authorize?response_type=code&client_id=_RN0elf1uUxGvpdRQram0s_hfPpOkimHVXhlHN5Cx5I&redirect_uri=https://127.0.0.1/oauth&scope=openid 13 | // 2. get the CODE from the url 14 | // 3. POST 'https://master.apis.dev.openstreetmap.org/oauth2/token?grant_type=authorization_code&code=&client_id=_RN0elf1uUxGvpdRQram0s_hfPpOkimHVXhlHN5Cx5I&redirect_uri=https://127.0.0.1/oauth' 15 | private static final String ALLOW_NOTHING_TOKEN = "Nyj_dTRKE9o5Oyj5nzPuC9EbB-B9q_MlVM73QYeWP0o"; 16 | 17 | private static final String UNKNOWN_TOKEN = "unknown"; 18 | 19 | private static final String TEST_API_URL = "https://master.apis.dev.openstreetmap.org/api/0.6/"; 20 | 21 | private static final String LIVE_API_URL = "https://api.openstreetmap.org/api/0.6/"; 22 | 23 | public static final String USER_AGENT = "osmapi unit test"; 24 | 25 | public enum User 26 | { 27 | ALLOW_EVERYTHING, ALLOW_NOTHING, UNKNOWN 28 | } 29 | 30 | public static OsmConnection createLiveConnection() 31 | { 32 | return new OsmConnection(LIVE_API_URL, USER_AGENT, null); 33 | } 34 | 35 | public static OsmConnection createConnection(User user) 36 | { 37 | String accessToken = ""; 38 | if(user == User.ALLOW_EVERYTHING) 39 | accessToken = ALLOW_EVERYTHING_TOKEN; 40 | else if(user == User.ALLOW_NOTHING) 41 | accessToken = ALLOW_NOTHING_TOKEN; 42 | else if(user == User.UNKNOWN) 43 | accessToken = UNKNOWN_TOKEN; 44 | 45 | return new OsmConnection(TEST_API_URL, USER_AGENT, accessToken); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /libs/traces/src/main/java/de/westnordost/osmapi/traces/GpsTracesParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.text.ParseException; 6 | import java.time.Instant; 7 | import java.util.ArrayList; 8 | import java.util.Locale; 9 | 10 | import de.westnordost.osmapi.ApiResponseReader; 11 | import de.westnordost.osmapi.common.Handler; 12 | import de.westnordost.osmapi.common.XmlParser; 13 | import de.westnordost.osmapi.map.data.OsmLatLon; 14 | 15 | /** Parses the GPS traces response of the osm api (API 0.6). */ 16 | public class GpsTracesParser extends XmlParser implements ApiResponseReader 17 | { 18 | 19 | private static final String 20 | GPX_FILE = "gpx_file", 21 | TAG = "tag", 22 | DESCRIPTION = "description"; 23 | 24 | private final Handler handler; 25 | private GpsTraceDetails trace; 26 | 27 | public GpsTracesParser(Handler handler) 28 | { 29 | this.handler = handler; 30 | } 31 | 32 | @Override 33 | public Void parse(InputStream in) throws IOException 34 | { 35 | doParse(in); 36 | return null; 37 | } 38 | 39 | @Override 40 | protected void onStartElement() throws ParseException 41 | { 42 | if(getName().equals(GPX_FILE)) 43 | { 44 | trace = new GpsTraceDetails(); 45 | trace.id = getLongAttribute("id"); 46 | trace.visibility = GpsTraceDetails.Visibility.valueOf(getAttribute("visibility").toUpperCase(Locale.UK)); 47 | 48 | trace.name = getAttribute("name"); 49 | trace.userName = getAttribute("user"); 50 | 51 | Boolean pendingAttribute = getBooleanAttribute("pending"); 52 | trace.pending = pendingAttribute == null || pendingAttribute; 53 | 54 | String lat = getAttribute("lat"); 55 | String lon = getAttribute("lon"); 56 | if(lat != null && lon != null) 57 | { 58 | trace.position = OsmLatLon.parseLatLon(lat, lon); 59 | } 60 | 61 | String timestamp = getAttribute("timestamp"); 62 | if(timestamp != null) 63 | trace.createdAt = Instant.parse(timestamp); 64 | } 65 | } 66 | 67 | @Override 68 | protected void onEndElement() 69 | { 70 | String name = getName(); 71 | 72 | if(name.equals(GPX_FILE)) 73 | { 74 | handler.handle(trace); 75 | trace = null; 76 | } 77 | else if(name.equals(DESCRIPTION)) 78 | { 79 | trace.description = getText(); 80 | } 81 | else if(name.equals(TAG)) 82 | { 83 | if(trace.tags == null) trace.tags = new ArrayList<>(); 84 | trace.tags.add(getText()); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /libs/changesets/src/main/java/de/westnordost/osmapi/changesets/ChangesetInfo.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.changesets; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import de.westnordost.osmapi.map.data.BoundingBox; 9 | 10 | /** Includes information for a changeset inclusive the changeset discussion but exclusive the 11 | * elements that were changed in the changeset. 12 | */ 13 | public class ChangesetInfo extends Changeset implements Serializable 14 | { 15 | private static final long serialVersionUID = 2L; 16 | 17 | /** map of tags associated with this changeset. May be null if there are no tags at all. */ 18 | public Map tags; 19 | 20 | /** The changeset discussion. May be null if there is none */ 21 | public List discussion; 22 | /** the number of notes in the changeset discussion of this changeset */ 23 | public int notesCount; 24 | /** the number of changes in a changeset */ 25 | public int changesCount; 26 | 27 | /** the bounding box that includes all changes of this changeset. May be null if the changeset 28 | * is empty. */ 29 | public BoundingBox boundingBox; 30 | 31 | public boolean isOpen; 32 | 33 | /** the date the changeset was closed. Null if the changeset is still open */ 34 | public Instant closedAt; 35 | 36 | /** the date the changeset was created. Null if the changeset is still open */ 37 | public Instant createdAt; 38 | 39 | /** A shortcut to getTags().get("comment") 40 | * @return the "commit comment" of the changeset which should include information about what was 41 | * changed (and why). null if none supplied. 42 | * */ 43 | public String getChangesetComment() 44 | { 45 | return tags != null ? tags.get("comment") : null; 46 | } 47 | 48 | /** A shortcut to getTags().get("source").split(";") 49 | * @return the source of the data entered. Common values include "bing", "survey", "local 50 | * knowledge", "common knowledge", "extrapolation", "photograph" (and more). null if 51 | * none supplied. 52 | * */ 53 | public String[] getSources() 54 | { 55 | String source = tags != null ? tags.get("source") : null; 56 | return source != null ? source.split("(\\s)?;(\\s)?") : null; 57 | } 58 | 59 | /** A shortcut to getTags().get("created_by") 60 | * @return the application with which this changeset has been created. null if none supplied. */ 61 | public String getGenerator() 62 | { 63 | return tags != null ? tags.get("created_by") : null; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /libs/user/src/main/java/de/westnordost/osmapi/user/UserApi.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import java.util.Collection; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import de.westnordost.osmapi.OsmConnection; 8 | import de.westnordost.osmapi.common.ListHandler; 9 | import de.westnordost.osmapi.common.SingleElementHandler; 10 | import de.westnordost.osmapi.common.errors.OsmNotFoundException; 11 | import de.westnordost.osmapi.common.errors.OsmAuthorizationException; 12 | 13 | /** Get user infos and details. 14 | * All interactions with this class require an OsmConnection with a logged in user. */ 15 | public class UserApi 16 | { 17 | private final OsmConnection osm; 18 | 19 | public UserApi(OsmConnection osm) 20 | { 21 | this.osm = osm; 22 | } 23 | 24 | /** @return the user info of the current user 25 | * @throws OsmAuthorizationException if the user does not have the permission 26 | * Permission.READ_PREFERENCES_AND_USER_DETAILS*/ 27 | public UserDetails getMine() 28 | { 29 | SingleElementHandler handler = new SingleElementHandler<>(); 30 | osm.makeAuthenticatedRequest("user/details", null, new UserDetailsParser(handler)); 31 | return (UserDetails) handler.get(); 32 | } 33 | 34 | /** 35 | * @param userId id of the user to get the user info for 36 | * @return the user info of the given user. Null if the user does not exist. 37 | * */ 38 | public UserInfo get(long userId) 39 | { 40 | try 41 | { 42 | SingleElementHandler handler = new SingleElementHandler<>(); 43 | boolean authenticate = osm.getOAuthAccessToken() != null; 44 | osm.makeRequest("user/" + userId, authenticate, new UserInfoParser(handler)); 45 | return handler.get(); 46 | } 47 | catch(OsmNotFoundException e) 48 | { 49 | return null; 50 | } 51 | } 52 | 53 | public List getAll(Collection userIds) 54 | { 55 | if(userIds.isEmpty()) return Collections.emptyList(); 56 | ListHandler handler = new ListHandler<>(); 57 | boolean authenticate = osm.getOAuthAccessToken() != null; 58 | osm.makeRequest("users?users=" + toCommaList(userIds), authenticate, new UserInfoParser(handler)); 59 | return handler.get(); 60 | } 61 | 62 | private static String toCommaList(Iterable vals) 63 | { 64 | StringBuilder result = new StringBuilder(); 65 | boolean first = true; 66 | for(Long id : vals) 67 | { 68 | if(id == null) continue; 69 | 70 | if(first) first = false; 71 | else result.append(","); 72 | result.append(id); 73 | } 74 | return result.toString(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /libs/build.gradle: -------------------------------------------------------------------------------- 1 | subprojects { 2 | apply plugin: 'maven' 3 | apply plugin: 'signing' 4 | 5 | repositories { 6 | mavenLocal() 7 | mavenCentral() 8 | } 9 | 10 | task javadocJar(type: Jar) { 11 | classifier = 'javadoc' 12 | from javadoc 13 | } 14 | 15 | task sourcesJar(type: Jar) { 16 | classifier = 'sources' 17 | from sourceSets.main.allSource 18 | } 19 | 20 | artifacts { 21 | archives javadocJar, sourcesJar 22 | } 23 | 24 | signing { 25 | sign configurations.archives 26 | } 27 | 28 | dependencies { 29 | testCompile 'junit:junit:4.13.2' 30 | testCompile project(':testutils') 31 | } 32 | 33 | uploadArchives { 34 | repositories { 35 | mavenDeployer { 36 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 37 | 38 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 39 | authentication(userName: ossrhUsername, password: ossrhPassword) 40 | } 41 | 42 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { 43 | authentication(userName: ossrhUsername, password: ossrhPassword) 44 | } 45 | 46 | pom.project { 47 | name archivesBaseName 48 | packaging 'jar' 49 | description description 50 | url 'https://github.com/westnordost/osmapi' 51 | 52 | scm { 53 | connection 'https://github.com/westnordost/osmapi.git' 54 | developerConnection 'https://github.com/westnordost/osmapi.git' 55 | url 'https://github.com/westnordost/osmapi' 56 | } 57 | 58 | licenses { 59 | license { 60 | name 'GNU Lesser General Public License, Version 3.0' 61 | url 'http://www.gnu.org/licenses/lgpl-3.0.html' 62 | } 63 | } 64 | 65 | developers { 66 | developer { 67 | id 'westnordost' 68 | name 'Tobias Zwick' 69 | email 'osm@westnordost.de' 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/FormDataWriter.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.io.OutputStreamWriter; 6 | import java.io.PrintWriter; 7 | import java.util.Locale; 8 | 9 | import de.westnordost.osmapi.ApiRequestWriter; 10 | 11 | /** Writes the payload for a multipart/form-data form.
12 | * Override {@link #write()} and add your form fields with {@link #addField(String, String)} and 13 | * {@link #addFileField(String, String, ApiRequestWriter)} there. */ 14 | public abstract class FormDataWriter implements ApiRequestWriter 15 | { 16 | private static final String CHARSET = "UTF-8"; 17 | private static final String LINE_FEED = "\r\n"; 18 | 19 | final String boundary; 20 | 21 | private PrintWriter printer; 22 | private OutputStream out; 23 | 24 | public FormDataWriter() 25 | { 26 | boundary = "---------------------------" + System.currentTimeMillis(); 27 | } 28 | 29 | @Override 30 | public String getContentType() 31 | { 32 | return "multipart/form-data; boundary=" + boundary; 33 | } 34 | 35 | @Override 36 | public final void write(OutputStream out) throws IOException 37 | { 38 | printer = new PrintWriter(new OutputStreamWriter(out, CHARSET)); 39 | this.out = out; 40 | write(); 41 | finish(); 42 | } 43 | 44 | protected abstract void write() throws IOException; 45 | 46 | private void finish() 47 | { 48 | printer.flush(); 49 | println("--" + boundary + "--"); 50 | printer.close(); 51 | } 52 | 53 | protected final void addField(String name, String value) 54 | { 55 | println("--" + boundary); 56 | println("Content-Disposition: form-data; name=\"" + name + "\""); 57 | println("Content-Type: text/plain; charset="+CHARSET.toLowerCase(Locale.UK)); 58 | println(); 59 | println(value); 60 | printer.flush(); 61 | } 62 | 63 | protected final void addFileField(String name, String fileName, ApiRequestWriter subWriter) 64 | throws IOException 65 | { 66 | println("--" + boundary); 67 | println("Content-Disposition: form-data; name=\"" + name + "\"; " + 68 | "filename=\"" + fileName + "\""); 69 | println("Content-Type: " + subWriter.getContentType()); 70 | println(); 71 | printer.flush(); 72 | subWriter.write(out); 73 | println(); 74 | printer.flush(); 75 | } 76 | 77 | private void println() 78 | { 79 | printer.append(LINE_FEED); 80 | } 81 | 82 | private void println(CharSequence text) 83 | { 84 | printer.append(text); 85 | println(); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /libs/traces/src/main/java/de/westnordost/osmapi/traces/GpxTrackParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.text.ParseException; 6 | import java.time.Instant; 7 | 8 | import de.westnordost.osmapi.ApiResponseReader; 9 | import de.westnordost.osmapi.common.Handler; 10 | import de.westnordost.osmapi.common.XmlParser; 11 | import de.westnordost.osmapi.map.data.LatLon; 12 | import de.westnordost.osmapi.map.data.OsmLatLon; 13 | 14 | /** Parses the trackpoints from response of the osm api (API 0.6). the osm api response is in the 15 | * form of a GPX, but we do not parse the whole GPX here but only the information that is necessary 16 | * for the trackpoints. Hence the name "GpxTrackParser" and not "GPXParser". */ 17 | public class GpxTrackParser extends XmlParser implements ApiResponseReader 18 | { 19 | private static final String 20 | TRACKPOINT = "trkpt", 21 | TRACKSEGMENT = "trkseg"; 22 | 23 | private final Handler handler; 24 | 25 | private boolean isFirstPointInTrackSegment = false; 26 | 27 | private LatLon position; 28 | private Instant time; 29 | 30 | private Float horizontalDilutionOfPrecision; 31 | private Float elevation; 32 | 33 | public GpxTrackParser(Handler handler) 34 | { 35 | this.handler = handler; 36 | } 37 | 38 | @Override 39 | public Void parse(InputStream in) throws IOException 40 | { 41 | doParse(in); 42 | return null; 43 | } 44 | 45 | @Override 46 | protected void onStartElement() throws ParseException 47 | { 48 | String name = getName(); 49 | 50 | if(name.equals(TRACKSEGMENT)) 51 | { 52 | isFirstPointInTrackSegment = true; 53 | } 54 | else if(name.equals(TRACKPOINT)) 55 | { 56 | position = OsmLatLon.parseLatLon(getAttribute("lat"), getAttribute("lon")); 57 | } 58 | } 59 | 60 | @Override 61 | protected void onEndElement() throws ParseException 62 | { 63 | String name = getName(); 64 | 65 | if(TRACKPOINT.equals(name)) 66 | { 67 | handler.handle(new GpsTrackpoint( 68 | position, time, isFirstPointInTrackSegment, horizontalDilutionOfPrecision, elevation 69 | )); 70 | position = null; 71 | time = null; 72 | isFirstPointInTrackSegment = false; 73 | horizontalDilutionOfPrecision = null; 74 | elevation = null; 75 | } 76 | else if(TRACKPOINT.equals(getParentName())) 77 | { 78 | if(name.equals("time")) time = Instant.parse(getText()); 79 | if(name.equals("ele")) elevation = Float.valueOf(getText()); 80 | if(name.equals("hdop")) horizontalDilutionOfPrecision = Float.valueOf(getText()); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/OsmElement.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import de.westnordost.osmapi.changesets.Changeset; 9 | 10 | /** 11 | * Base class for the osm primitives nodes, ways and relations 12 | */ 13 | public abstract class OsmElement implements Element, Serializable 14 | { 15 | private static final long serialVersionUID = 1L; 16 | 17 | private long id; 18 | private int version; 19 | private Changeset changeset; 20 | private Instant editedAt; 21 | private OsmTags tags; 22 | private boolean deleted; 23 | private boolean created; 24 | private boolean modified; 25 | 26 | public OsmElement(long id, int version, Map tags) 27 | { 28 | this(id,version,tags,null,null); 29 | } 30 | 31 | public OsmElement(long id, int version, Map tags, Changeset changeset, Instant editedAt) 32 | { 33 | this.id = id; 34 | this.version = version; 35 | this.changeset = changeset; 36 | this.tags = tags != null ? new OsmTags(tags) : new OsmTags(new HashMap(0)); 37 | this.editedAt = editedAt; 38 | } 39 | 40 | @Override 41 | public long getId() 42 | { 43 | return id; 44 | } 45 | 46 | @Override 47 | public Changeset getChangeset() 48 | { 49 | return changeset; 50 | } 51 | 52 | @Override 53 | public int getVersion() 54 | { 55 | return version; 56 | } 57 | 58 | @Override 59 | public Map getTags() 60 | { 61 | return tags; 62 | } 63 | 64 | public void setTags(Map tags) 65 | { 66 | modified = true; 67 | this.tags = tags != null ? new OsmTags(tags) : null; 68 | } 69 | 70 | @Override 71 | public boolean isNew() 72 | { 73 | return id < 0 || created; 74 | } 75 | 76 | public void setNew(boolean isNew) 77 | { 78 | this.created = isNew; 79 | } 80 | 81 | @Override 82 | public boolean isModified() 83 | { 84 | return modified || tags != null && tags.isModified(); 85 | } 86 | 87 | public void setModified(boolean modified) 88 | { 89 | this.modified = modified; 90 | } 91 | 92 | @Override 93 | public boolean isDeleted() 94 | { 95 | return deleted; 96 | } 97 | 98 | public void setDeleted(boolean deleted) 99 | { 100 | this.deleted = deleted; 101 | } 102 | 103 | @Override 104 | public abstract Type getType(); 105 | 106 | public Instant getEditedAt() 107 | { 108 | return editedAt; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/MapDataFactory.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map; 2 | 3 | import java.time.Instant; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import de.westnordost.osmapi.changesets.Changeset; 8 | import de.westnordost.osmapi.map.data.Element; 9 | import de.westnordost.osmapi.map.data.Node; 10 | import de.westnordost.osmapi.map.data.Relation; 11 | import de.westnordost.osmapi.map.data.RelationMember; 12 | import de.westnordost.osmapi.map.data.Way; 13 | 14 | public interface MapDataFactory 15 | { 16 | /** Create a node from the given data. 17 | * @param id id of the node 18 | * @param version version of the node 19 | * @param lat latitude position of the node 20 | * @param lon longitude position of the node 21 | * @param tags tags of the node. May be null. 22 | * @param changeset changeset in which the node was last updated. May be null. 23 | * @param editedAt time at which the node was last updated. May be null. 24 | * @return the node */ 25 | Node createNode(long id, int version, Double lat, Double lon, Map tags, 26 | Changeset changeset, Instant editedAt); 27 | 28 | /** Create a way from the given data 29 | * @param id id of the wy 30 | * @param version version of the way 31 | * @param nodes list of nodes this way consists of 32 | * @param tags tags of the way. May be null. 33 | * @param changeset changeset in which the way was last updated. May be null. 34 | * @param editedAt time at which the way was last updated. May be null. 35 | * @return the way 36 | */ 37 | Way createWay(long id, int version, List nodes, Map tags, 38 | Changeset changeset, Instant editedAt); 39 | 40 | /** Create a relation from the given data 41 | * @param id id of the relation 42 | * @param version version of the relation 43 | * @param members list of members this relation consists of 44 | * @param tags tags of the relation. May be null. 45 | * @param changeset changeset in which the relation was last updated. May be null. 46 | * @param editedAt time at which the relation was last updated. May be null. 47 | * @return the relation */ 48 | Relation createRelation(long id, int version, List members, 49 | Map tags, Changeset changeset, Instant editedAt); 50 | 51 | /** Create a relation member from the given data 52 | * @param ref id of the member node, way or relation 53 | * @param role role of the member 54 | * @param type element type of the member, either node, way or relation 55 | * @return the relation member */ 56 | RelationMember createRelationMember(long ref, String role, Element.Type type); 57 | } 58 | -------------------------------------------------------------------------------- /libs/traces/src/main/java/de/westnordost/osmapi/traces/GpxTrackWriter.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import java.io.IOException; 4 | import java.time.format.DateTimeFormatter; 5 | 6 | import de.westnordost.osmapi.common.XmlWriter; 7 | 8 | public class GpxTrackWriter extends XmlWriter 9 | { 10 | private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT; 11 | 12 | private final String userAgent; 13 | private final Iterable trackpoints; 14 | 15 | public GpxTrackWriter(String userAgent, Iterable trackpoints) 16 | { 17 | this.userAgent = userAgent; 18 | this.trackpoints = trackpoints; 19 | } 20 | 21 | @Override 22 | protected void write() throws IOException 23 | { 24 | begin("gpx"); 25 | attribute("version", 1.0); 26 | attribute("creator", userAgent); 27 | attribute("xmlns","http://www.topografix.com/GPX/1/0"); 28 | attribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); 29 | attribute("xsi:schemaLocation","http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd"); 30 | 31 | writeTrack(); 32 | 33 | end(); 34 | } 35 | 36 | private void writeTrack() throws IOException 37 | { 38 | begin("trk"); 39 | 40 | boolean isVeryFirst = true; 41 | int segmentCount = 0; 42 | for(GpsTrackpoint trackpoint : trackpoints) 43 | { 44 | if(trackpoint.isFirstPointInTrackSegment || isVeryFirst) 45 | { 46 | if(!isVeryFirst) 47 | { 48 | end(); 49 | } 50 | begin("trkseg"); 51 | segmentCount++; 52 | isVeryFirst = false; 53 | } 54 | 55 | writeTrackpoint(trackpoint); 56 | } 57 | if(segmentCount > 0) 58 | { 59 | end(); 60 | } 61 | 62 | end(); 63 | } 64 | 65 | private void writeTrackpoint(GpsTrackpoint trackpoint) throws IOException 66 | { 67 | begin("trkpt"); 68 | attribute("lat", trackpoint.position.getLatitude()); 69 | attribute("lon", trackpoint.position.getLongitude()); 70 | 71 | begin("time"); 72 | text(FORMATTER.format(trackpoint.time)); 73 | end(); 74 | 75 | if(trackpoint.elevation != null) 76 | { 77 | begin("ele"); 78 | float ele1Decimal = roundToOneDecimal(trackpoint.elevation); 79 | text(String.valueOf(ele1Decimal)); 80 | end(); 81 | } 82 | if(trackpoint.horizontalDilutionOfPrecision != null) 83 | { 84 | begin("hdop"); 85 | float hdop1Decimal = roundToOneDecimal(trackpoint.horizontalDilutionOfPrecision); 86 | text(String.valueOf(hdop1Decimal)); 87 | end(); 88 | } 89 | end(); 90 | } 91 | 92 | private static float roundToOneDecimal(float x) 93 | { 94 | return Math.round(10*x)/10f; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/capabilities/Capabilities.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.capabilities; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Locale; 6 | 7 | /** (Current) capabilities of the osm server. */ 8 | public class Capabilities implements Serializable 9 | { 10 | private static final long serialVersionUID = 1L; 11 | 12 | public float minSupportedApiVersion; 13 | public float maxSupportedApiVersion; 14 | 15 | public float maxMapQueryAreaInSquareDegrees; 16 | public float maxNotesQueryAreaInSquareDegrees; 17 | 18 | public int maxNodesInWay; 19 | public int maxMembersInRelation; 20 | /** See http://wiki.openstreetmap.org/wiki/API_v0.6#Retrieving_GPS_points */ 21 | public int maxPointsInGpsTracePerPage; 22 | public int maxElementsPerChangeset; 23 | 24 | public int timeoutInSeconds; 25 | 26 | public ApiStatus databaseStatus; 27 | public ApiStatus mapDataStatus; 28 | public ApiStatus gpsTracesStatus; 29 | 30 | /** list of regular expressions to match URLs from which the use of imagery is prohibited 31 | * explicitly by the API. Naturally, this is not an exhaustive list, it is only to 32 | * save users from attempting to use imagery from sources obviously not allowed for OSM. 33 | * I.e. anything google. 34 | * Note that if the server did not send this information, this method returns null. */ 35 | public List imageryBlacklistRegExes; 36 | 37 | public int defaultNotesQueryLimit; 38 | public int maximumNotesQueryLimit; 39 | public int defaultChangesetsQueryLimit; 40 | public int maximumChangesetsQueryLimit; 41 | 42 | public enum ApiStatus 43 | { 44 | ONLINE, 45 | OFFLINE, 46 | READONLY 47 | } 48 | 49 | static ApiStatus parseApiStatus(String status) 50 | { 51 | return ApiStatus.valueOf(status.toUpperCase(Locale.UK)); 52 | } 53 | 54 | /* Convenience getters */ 55 | 56 | /** @return whether the data in the database can be modified. Implies that it can also be read. */ 57 | public boolean isDatabaseWritable() 58 | { 59 | return databaseStatus == ApiStatus.ONLINE; 60 | } 61 | 62 | public boolean isDatabaseReadable() 63 | { 64 | return databaseStatus != ApiStatus.OFFLINE; 65 | } 66 | 67 | public boolean isMapDataModifiable() 68 | { 69 | return mapDataStatus == ApiStatus.ONLINE; 70 | } 71 | 72 | public boolean isMapDataReadable() 73 | { 74 | return mapDataStatus != ApiStatus.OFFLINE; 75 | } 76 | 77 | public boolean isGpsTracesUploadable() 78 | { 79 | return gpsTracesStatus == ApiStatus.ONLINE; 80 | } 81 | 82 | public boolean isGpsTracesReadable() 83 | { 84 | return gpsTracesStatus != ApiStatus.OFFLINE; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /libs/user/src/main/java/de/westnordost/osmapi/user/UserDetailsParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import java.text.ParseException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import de.westnordost.osmapi.common.Handler; 8 | import de.westnordost.osmapi.map.data.OsmLatLon; 9 | 10 | /** Parses private information for a user */ 11 | public class UserDetailsParser extends UserInfoParser 12 | { 13 | private static final String USER = "user", 14 | LANGUAGES = "languages"; 15 | 16 | private List languages; 17 | 18 | private UserDetails userDetails; 19 | 20 | public UserDetailsParser(Handler handler) 21 | { 22 | super(handler); 23 | } 24 | 25 | @Override 26 | protected void createUser(long id, String name) 27 | { 28 | user = userDetails = new UserDetails(id, name); 29 | } 30 | 31 | @Override 32 | protected void onStartElement() throws ParseException 33 | { 34 | super.onStartElement(); 35 | 36 | String name = getName(); 37 | String parent = getParentName(); 38 | 39 | if(LANGUAGES.equals(name)) 40 | { 41 | languages = new ArrayList<>(); 42 | } 43 | else if(USER.equals(parent)) 44 | { 45 | switch(name) 46 | { 47 | case "contributor-terms": 48 | // pd is optional 49 | Boolean publicDomain = getBooleanAttribute("pd"); 50 | if(publicDomain != null) 51 | userDetails.considersHisContributionsAsPublicDomain = publicDomain; 52 | break; 53 | case "home": 54 | userDetails.homeLocation = OsmLatLon.parseLatLon(getAttribute("lat"), getAttribute("lon")); 55 | userDetails.homeZoom = getByteAttribute("zoom"); 56 | break; 57 | } 58 | } 59 | else if("messages".equals(parent)) 60 | { 61 | switch(name) 62 | { 63 | case "received": 64 | userDetails.inboxMessageCount = getIntAttribute("count"); 65 | userDetails.unreadMessagesCount = getIntAttribute("unread"); 66 | break; 67 | case "sent": 68 | userDetails.sentMessagesCount = getIntAttribute("count"); 69 | break; 70 | } 71 | } 72 | } 73 | 74 | @Override 75 | protected void onEndElement() 76 | { 77 | String name = getName(); 78 | String parent = getParentName(); 79 | 80 | if(USER.equals(name)) 81 | { 82 | handler.handle(userDetails); 83 | user = userDetails = null; 84 | } 85 | else if(LANGUAGES.equals(name)) 86 | { 87 | userDetails.preferredLanguages = languages; 88 | languages = null; 89 | } 90 | if(LANGUAGES.equals(parent)) 91 | { 92 | if("lang".equals(name)) 93 | { 94 | assert languages != null; 95 | languages.add(getText()); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/changes/SimpleMapDataChangesHandler.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.changes; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import de.westnordost.osmapi.map.data.BoundingBox; 7 | import de.westnordost.osmapi.map.data.Element; 8 | import de.westnordost.osmapi.map.data.Node; 9 | import de.westnordost.osmapi.map.data.Relation; 10 | import de.westnordost.osmapi.map.data.Way; 11 | 12 | public class SimpleMapDataChangesHandler implements MapDataChangesHandler, MapDataChanges 13 | { 14 | private final List creations; 15 | private final List modifications; 16 | private final List deletions; 17 | private Mode mode; 18 | private enum Mode 19 | { 20 | CREATIONS, MODIFICATIONS, DELETIONS 21 | } 22 | 23 | public SimpleMapDataChangesHandler() 24 | { 25 | creations = new ArrayList<>(); 26 | modifications = new ArrayList<>(); 27 | deletions = new ArrayList<>(); 28 | } 29 | 30 | @Override 31 | public void onStartCreations() 32 | { 33 | mode = Mode.CREATIONS; 34 | } 35 | 36 | @Override 37 | public void onStartModifications() 38 | { 39 | mode = Mode.MODIFICATIONS; 40 | } 41 | 42 | @Override 43 | public void onStartDeletions() 44 | { 45 | mode = Mode.DELETIONS; 46 | } 47 | 48 | @Override 49 | public void handle(BoundingBox bounds) 50 | { 51 | // ignore, not interested in that... 52 | } 53 | 54 | @Override 55 | public void handle(Node node) 56 | { 57 | handleElement(node); 58 | } 59 | 60 | @Override 61 | public void handle(Way way) 62 | { 63 | handleElement(way); 64 | } 65 | 66 | @Override 67 | public void handle(Relation relation) 68 | { 69 | handleElement(relation); 70 | } 71 | 72 | private void handleElement(Element element) 73 | { 74 | switch(mode) 75 | { 76 | case CREATIONS: 77 | creations.add(element); 78 | break; 79 | case MODIFICATIONS: 80 | modifications.add(element); 81 | break; 82 | case DELETIONS: 83 | deletions.add(element); 84 | break; 85 | } 86 | } 87 | 88 | public List getDeletions() 89 | { 90 | return deletions; 91 | } 92 | 93 | public List getModifications() 94 | { 95 | return modifications; 96 | } 97 | 98 | public List getCreations() 99 | { 100 | return creations; 101 | } 102 | 103 | public List getAll() 104 | { 105 | List result = new ArrayList<>(); 106 | result.addAll(creations); 107 | result.addAll(modifications); 108 | result.addAll(deletions); 109 | return result; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /libs/user/src/test/java/de/westnordost/osmapi/user/UserPreferencesApiTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import de.westnordost.osmapi.ConnectionTestFactory; 10 | import de.westnordost.osmapi.common.errors.OsmAuthorizationException; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | public class UserPreferencesApiTest 15 | { 16 | private UserPreferencesApi privilegedApi; 17 | private UserPreferencesApi unprivilegedApi; 18 | 19 | @Before public void setUp() 20 | { 21 | privilegedApi = new UserPreferencesApi(ConnectionTestFactory.createConnection( 22 | ConnectionTestFactory.User.ALLOW_EVERYTHING)); 23 | unprivilegedApi = new UserPreferencesApi(ConnectionTestFactory.createConnection( 24 | ConnectionTestFactory.User.ALLOW_NOTHING)); 25 | } 26 | 27 | @Test public void unprivileged() 28 | { 29 | Class e = OsmAuthorizationException.class; 30 | // the unprivileged api may do nothing here, so lets just do it in one test case... 31 | assertThrows(e, () -> unprivilegedApi.delete("A")); 32 | assertThrows(e, () -> unprivilegedApi.get("A")); 33 | assertThrows(e, () -> unprivilegedApi.getAll()); 34 | assertThrows(e, () -> unprivilegedApi.set("A","a")); 35 | assertThrows(e, () -> unprivilegedApi.setAll(new HashMap<>())); 36 | assertThrows(e, () -> unprivilegedApi.delete("A")); 37 | assertThrows(e, () -> unprivilegedApi.delete("A")); 38 | assertThrows(e, () -> unprivilegedApi.delete("A")); 39 | assertThrows(e, () -> unprivilegedApi.delete("A")); 40 | assertThrows(e, () -> unprivilegedApi.delete("A")); 41 | } 42 | 43 | @Test public void setGetAndDeleteUserPreference() 44 | { 45 | privilegedApi.set("A","a"); 46 | assertEquals("a",privilegedApi.get("A")); 47 | privilegedApi.delete("A"); 48 | assertNull(privilegedApi.get("A")); 49 | } 50 | 51 | @Test public void keyTooLong() 52 | { 53 | assertThrows(IllegalArgumentException.class, () -> privilegedApi.set(tooLong(), "jo")); 54 | } 55 | 56 | @Test public void valueTooLong() 57 | { 58 | assertThrows(IllegalArgumentException.class, () -> privilegedApi.set("jo", tooLong())); 59 | } 60 | 61 | private static String tooLong() 62 | { 63 | String result = ""; 64 | for(int i=0; i<=256; ++i) result += "x"; 65 | return result; 66 | } 67 | 68 | @Test public void setAndGetUserPreferences() 69 | { 70 | Map preferences = new HashMap<>(); 71 | preferences.put("D", "d"); 72 | preferences.put("E", "e"); 73 | 74 | privilegedApi.setAll(preferences); 75 | Map updatedPreferences = privilegedApi.getAll(); 76 | assertEquals(preferences, updatedPreferences); 77 | 78 | // deleting a previously set user preferences by omitting it 79 | preferences.remove("D"); 80 | 81 | privilegedApi.setAll(preferences); 82 | updatedPreferences = privilegedApi.getAll(); 83 | assertEquals(preferences, updatedPreferences); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /libs/messages/src/test/java/de/westnordost/osmapi/messages/MessagesParserTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.messages; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertNull; 6 | import static org.junit.Assert.assertTrue; 7 | 8 | import org.junit.Test; 9 | 10 | import java.io.IOException; 11 | import java.time.Instant; 12 | 13 | import de.westnordost.osmapi.TestUtils; 14 | import de.westnordost.osmapi.common.ListHandler; 15 | import de.westnordost.osmapi.common.SingleElementHandler; 16 | 17 | public class MessagesParserTest { 18 | @Test public void basicElements() throws IOException 19 | { 20 | String xml = 21 | "" + 22 | ""; 23 | 24 | Message message = parseOne(xml); 25 | assertEquals(777L, message.id); 26 | assertNull(message.read); 27 | assertFalse(message.deleted); 28 | assertEquals(Instant.parse("2019-06-15T08:26:04Z"), message.sentOn); 29 | assertEquals(222, message.toUser.id); 30 | assertEquals(111, message.fromUser.id); 31 | } 32 | 33 | @Test public void optionalElements() throws IOException 34 | { 35 | String xml = 36 | "" + 37 | " Title" + 38 | " Body" + 39 | ""; 40 | 41 | Message message = parseOne(xml); 42 | assertEquals(Message.BodyFormat.MARKDOWN, message.bodyFormat); 43 | assertEquals("Y", message.toUser.displayName); 44 | assertEquals("X", message.fromUser.displayName); 45 | assertTrue(message.read); 46 | assertTrue(message.deleted); 47 | assertEquals("Title", message.title); 48 | assertEquals("Body", message.body); 49 | } 50 | 51 | @Test public void parseMultiple() throws IOException 52 | { 53 | String xml = 54 | "" + 55 | "" + 56 | ""; 57 | 58 | ListHandler handler = new ListHandler<>(); 59 | new MessagesParser(handler).parse(TestUtils.asInputStream(xml)); 60 | assertEquals(3, handler.get().size()); 61 | } 62 | 63 | private Message parseOne(String xml) throws IOException 64 | { 65 | SingleElementHandler handler = new SingleElementHandler<>(); 66 | new MessagesParser(handler).parse(TestUtils.asInputStream(xml)); 67 | return handler.get(); 68 | } 69 | } -------------------------------------------------------------------------------- /libs/core/src/test/java/de/westnordost/osmapi/map/data/BoundingBoxTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.List; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | public class BoundingBoxTest 10 | { 11 | private static final double A = 34.1234, B = 12.1234, C = 37.1237, D = 15.1254; 12 | 13 | @Test public void validation4Doubles() 14 | { 15 | assertThrows(IllegalArgumentException.class, () -> new BoundingBox(0, 0, -1, 0)); 16 | } 17 | 18 | @Test public void validationLatLon() 19 | { 20 | assertThrows( 21 | IllegalArgumentException.class, 22 | () -> new BoundingBox(new OsmLatLon(0, 0), new OsmLatLon(-1, 0)) 23 | ); 24 | } 25 | 26 | @Test public void cross180thMeridian() 27 | { 28 | BoundingBox bounds = new BoundingBox( 29 | new OsmLatLon(0, 90), 30 | new OsmLatLon(1, -90)); 31 | assertTrue(bounds.crosses180thMeridian()); 32 | 33 | List boundses = bounds.splitAt180thMeridian(); 34 | BoundingBox bounds1 = boundses.get(0); 35 | BoundingBox bounds2 = boundses.get(1); 36 | 37 | assertEquals(bounds.getMin(),bounds1.getMin()); 38 | assertEquals(bounds.getMax().getLatitude(), bounds1.getMax().getLatitude(), 1e-7); 39 | assertEquals(LatLons.MAX_VALUE.getLongitude(), bounds1.getMax().getLongitude(), 1e-7); 40 | 41 | assertEquals(bounds.getMin().getLatitude(), bounds2.getMin().getLatitude(), 1e-7); 42 | assertEquals(LatLons.MIN_VALUE.getLongitude(), bounds2.getMin().getLongitude(), 1e-7); 43 | assertEquals(bounds.getMax(), bounds2.getMax()); 44 | } 45 | 46 | @Test public void testEquals() 47 | { 48 | BoundingBox bounds1 = new BoundingBox( 49 | OsmLatLon.parseLatLon("51.7400243", "0.2400123"), 50 | OsmLatLon.parseLatLon("55.7410243", "0.2701123")); 51 | BoundingBox bounds2 = new BoundingBox( 52 | OsmLatLon.parseLatLon("51.7400243", "0.2400123"), 53 | OsmLatLon.parseLatLon("55.7410243", "0.2701123")); 54 | assertEquals(bounds1, bounds2); 55 | } 56 | 57 | @Test public void equalsWithDifferentConstructors() 58 | { 59 | BoundingBox bounds1 = new BoundingBox(A,B,C,D); 60 | BoundingBox bounds2 = new BoundingBox(new OsmLatLon(A,B), new OsmLatLon(C,D)); 61 | 62 | assertEquals(bounds1, bounds2); 63 | } 64 | 65 | @Test public void equalsNull() 66 | { 67 | BoundingBox bounds1 = new BoundingBox(A,B,C,D); 68 | assertNotEquals(null, bounds1); 69 | } 70 | 71 | @Test public void equalsOtherObject() 72 | { 73 | BoundingBox bounds1 = new BoundingBox(A,B,C,D); 74 | assertNotEquals(bounds1, new Object()); 75 | } 76 | 77 | @Test public void doesNotUseScientificNotation() 78 | { 79 | BoundingBox bounds1 = new BoundingBox(-0.0000001,-0.0000001,0.0000001,0.0000001); 80 | assertEquals("-0.0000001,-0.0000001,0.0000001,0.0000001",bounds1.getAsLeftBottomRightTopString()); 81 | } 82 | 83 | @Test public void testHashCode() 84 | { 85 | BoundingBox bounds1 = new BoundingBox(A,B,C,D); 86 | BoundingBox bounds2 = new BoundingBox(A,B,C,D); 87 | assertEquals(bounds1.hashCode(), bounds2.hashCode()); 88 | } 89 | 90 | @Test public void hashCodeAlgoIsNotTooSimple() 91 | { 92 | BoundingBox bounds1 = new BoundingBox(A,B,C,D); 93 | BoundingBox bounds2 = new BoundingBox(B,A,D,C); 94 | 95 | assertNotEquals(bounds1.hashCode(), bounds2.hashCode()); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /libs/traces/src/test/java/de/westnordost/osmapi/traces/GpxTrackParserTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.IOException; 6 | import java.time.Instant; 7 | import java.util.List; 8 | 9 | import de.westnordost.osmapi.TestUtils; 10 | import de.westnordost.osmapi.common.Handler; 11 | import de.westnordost.osmapi.common.ListHandler; 12 | import de.westnordost.osmapi.common.SingleElementHandler; 13 | 14 | import static org.junit.Assert.*; 15 | 16 | public class GpxTrackParserTest 17 | { 18 | @Test public void parseEmptyTrack() 19 | { 20 | String xml = ""; 21 | assertNull(parseOne(xml)); 22 | } 23 | 24 | @Test public void parseSingleTrackpointWithExtras() 25 | { 26 | String xml = 27 | "" + 28 | "" + 29 | "789.1" + 30 | "" + 31 | "2.12" + 32 | "" + 33 | ""; 34 | 35 | GpsTrackpoint trackpoint = parseOne(xml); 36 | assertTrue(trackpoint.isFirstPointInTrackSegment); 37 | assertEquals(12.3, trackpoint.position.getLatitude(), 1e-7); 38 | assertEquals(45.6, trackpoint.position.getLongitude(), 1e-7); 39 | assertEquals(2.12f, trackpoint.horizontalDilutionOfPrecision, 1e-7); 40 | assertEquals(789.1f, trackpoint.elevation, 1e-7); 41 | 42 | assertEquals(Instant.parse("2016-04-17T16:41:02Z"), trackpoint.time); 43 | } 44 | 45 | @Test public void parseSingleTrackpointWithMillis() 46 | { 47 | String xml = 48 | "" + 49 | "" + 50 | "" + 51 | "" + 52 | ""; 53 | 54 | GpsTrackpoint trackpoint = parseOne(xml); 55 | 56 | assertEquals(Instant.parse("2016-04-17T16:41:02.654Z"), trackpoint.time); 57 | assertEquals(654, trackpoint.time.toEpochMilli() % 1000); 58 | } 59 | 60 | @Test public void parseMultipleSegments() 61 | { 62 | String xml = 63 | "" + 64 | "" + 65 | "" + 66 | "" + 67 | "" + 68 | "" + 69 | ""; 70 | 71 | List list = parseList(xml); 72 | assertEquals(3, list.size()); 73 | assertTrue(list.get(0).isFirstPointInTrackSegment); 74 | assertFalse(list.get(1).isFirstPointInTrackSegment); 75 | assertTrue(list.get(2).isFirstPointInTrackSegment); 76 | } 77 | 78 | private List parseList(String xml) 79 | { 80 | ListHandler handler = new ListHandler<>(); 81 | parse(xml, handler); 82 | return handler.get(); 83 | } 84 | 85 | private GpsTrackpoint parseOne(String xml) 86 | { 87 | SingleElementHandler handler = new SingleElementHandler<>(); 88 | parse(xml, handler); 89 | return handler.get(); 90 | } 91 | 92 | private void parse(String xml, Handler handler) 93 | { 94 | try 95 | { 96 | new GpxTrackParser(handler).parse(TestUtils.asInputStream(xml)); 97 | } 98 | catch(IOException e) 99 | { 100 | throw new RuntimeException(e); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /libs/map/src/test/java/de/westnordost/osmapi/map/MapDataHistoryApiTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import de.westnordost.osmapi.ConnectionTestFactory; 7 | import de.westnordost.osmapi.common.Handler; 8 | import de.westnordost.osmapi.common.errors.OsmNotFoundException; 9 | import de.westnordost.osmapi.map.data.Node; 10 | import de.westnordost.osmapi.map.data.Relation; 11 | import de.westnordost.osmapi.map.data.Way; 12 | 13 | import static org.junit.Assert.*; 14 | 15 | public class MapDataHistoryApiTest 16 | { 17 | private MapDataHistoryApi liveApi; 18 | private MapDataHistoryApi api; 19 | 20 | @Before public void setUp() 21 | { 22 | liveApi = new MapDataHistoryApi(ConnectionTestFactory.createLiveConnection()); 23 | api = new MapDataHistoryApi(ConnectionTestFactory.createConnection(null)); 24 | } 25 | 26 | @Test public void getUnknownNodeHistory() 27 | { 28 | assertThrows( 29 | OsmNotFoundException.class, 30 | () -> api.getNodeHistory(Long.MAX_VALUE, new NullHandler<>()) 31 | ); 32 | assertNull(api.getNodeVersion(Long.MAX_VALUE,1)); 33 | } 34 | 35 | @Test public void getUnknownWayHistory() 36 | { 37 | assertThrows( 38 | OsmNotFoundException.class, 39 | () -> api.getWayHistory(Long.MAX_VALUE, new NullHandler<>()) 40 | ); 41 | assertNull(api.getWayVersion(Long.MAX_VALUE, 1)); 42 | } 43 | 44 | @Test public void getUnknownRelationHistory() 45 | { 46 | assertThrows( 47 | OsmNotFoundException.class, 48 | () -> api.getRelationHistory(Long.MAX_VALUE, new NullHandler<>()) 49 | ); 50 | assertNull(api.getRelationVersion(Long.MAX_VALUE, 1)); 51 | } 52 | 53 | @Test public void getUnknownNodeVersion() 54 | { 55 | assertNull(liveApi.getNodeVersion(ElementShouldExist.NODE, Integer.MAX_VALUE)); 56 | } 57 | 58 | @Test public void getUnknownWayVersion() 59 | { 60 | assertNull(liveApi.getWayVersion(ElementShouldExist.WAY, Integer.MAX_VALUE)); 61 | } 62 | 63 | @Test public void getUnknownRelationVersion() 64 | { 65 | assertNull(liveApi.getRelationVersion(ElementShouldExist.RELATION, Integer.MAX_VALUE)); 66 | } 67 | 68 | @Test public void getNodeHistory() 69 | { 70 | CountHandler handler = new CountHandler<>(); 71 | liveApi.getNodeHistory(ElementShouldExist.NODE, handler); 72 | assertTrue(handler.count > 0); 73 | assertNotNull(liveApi.getNodeVersion(ElementShouldExist.NODE, 1)); 74 | } 75 | 76 | @Test public void getWayHistory() 77 | { 78 | CountHandler handler = new CountHandler<>(); 79 | liveApi.getWayHistory(ElementShouldExist.WAY, handler); 80 | assertTrue(handler.count > 0); 81 | assertNotNull(liveApi.getWayVersion(ElementShouldExist.WAY, 1)); 82 | } 83 | 84 | @Test public void getRelationHistory() 85 | { 86 | CountHandler handler = new CountHandler<>(); 87 | liveApi.getRelationHistory(ElementShouldExist.RELATION, handler); 88 | assertTrue(handler.count > 0); 89 | assertNotNull(liveApi.getRelationVersion(ElementShouldExist.RELATION, 1)); 90 | } 91 | 92 | private static class NullHandler implements Handler 93 | { 94 | public void handle(T tea) { } 95 | } 96 | 97 | private static class CountHandler implements Handler 98 | { 99 | int count; 100 | 101 | public void handle(T tea) { count++; } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /libs/core/src/test/java/de/westnordost/osmapi/capabilities/CapabilitiesParserTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.capabilities; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.IOException; 6 | 7 | import de.westnordost.osmapi.TestUtils; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | public class CapabilitiesParserTest 12 | { 13 | @Test public void basicFields() throws IOException 14 | { 15 | String xml = 16 | "" + 17 | " " + 18 | " " + 19 | " " + 20 | " " + 21 | " " + 22 | " " + 23 | " " + 24 | " " + 25 | " " + 26 | " " + 27 | ""; 28 | 29 | Capabilities capabilities = new CapabilitiesParser().parse(TestUtils.asInputStream(xml)); 30 | assertEquals(0.6f,capabilities.minSupportedApiVersion, 0.0); 31 | assertEquals(0.6f, capabilities.maxSupportedApiVersion, 0.0); 32 | assertEquals(0.25f, capabilities.maxMapQueryAreaInSquareDegrees, 0.0); 33 | assertEquals(25f, capabilities.maxNotesQueryAreaInSquareDegrees, 0.0); 34 | assertEquals(5000, capabilities.maxPointsInGpsTracePerPage); 35 | assertEquals(2000, capabilities.maxNodesInWay); 36 | assertEquals(32000, capabilities.maxMembersInRelation); 37 | assertEquals(50000, capabilities.maxElementsPerChangeset); 38 | assertEquals(300, capabilities.timeoutInSeconds); 39 | assertEquals(100, capabilities.defaultNotesQueryLimit); 40 | assertEquals(10000, capabilities.maximumNotesQueryLimit); 41 | assertEquals(100, capabilities.defaultChangesetsQueryLimit); 42 | assertEquals(200, capabilities.maximumChangesetsQueryLimit); 43 | } 44 | 45 | @Test public void apiStatus() throws IOException 46 | { 47 | String xml = 48 | "" + 49 | " " + 50 | ""; 51 | 52 | Capabilities capabilities = new CapabilitiesParser().parse(TestUtils.asInputStream(xml)); 53 | assertTrue(capabilities.isDatabaseReadable()); 54 | assertTrue(capabilities.isDatabaseWritable()); 55 | assertFalse(capabilities.isMapDataModifiable()); 56 | assertFalse(capabilities.isMapDataReadable()); 57 | assertTrue(capabilities.isGpsTracesReadable()); 58 | assertFalse(capabilities.isGpsTracesUploadable()); 59 | } 60 | 61 | @Test public void policy() throws IOException 62 | { 63 | String xml = 64 | "" + 65 | " " + 66 | " " + 67 | " " + 68 | " " + 69 | " " + 70 | ""; 71 | 72 | Capabilities capabilities = new CapabilitiesParser().parse(TestUtils.asInputStream(xml)); 73 | assertEquals(".*\\.googleapis\\.com/.*", capabilities.imageryBlacklistRegExes.get(0)); 74 | assertEquals(".*\\.google\\.com/.*", capabilities.imageryBlacklistRegExes.get(1)); 75 | assertEquals(".*\\.google\\.ru/.*", capabilities.imageryBlacklistRegExes.get(2)); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/XmlWriter.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common; 2 | 3 | 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.text.NumberFormat; 7 | import java.util.Locale; 8 | 9 | import org.xmlpull.v1.XmlPullParserException; 10 | import org.xmlpull.v1.XmlPullParserFactory; 11 | import org.xmlpull.v1.XmlSerializer; 12 | 13 | import de.westnordost.osmapi.ApiRequestWriter; 14 | 15 | /** 16 | * A simple XML writer / serializer with convenience method and less generic 17 | */ 18 | public abstract class XmlWriter implements ApiRequestWriter 19 | { 20 | private static final String CHARSET = "UTF-8"; 21 | 22 | private XmlSerializer xml; 23 | 24 | private final NumberFormat numberFormat; 25 | 26 | public XmlWriter() 27 | { 28 | numberFormat = NumberFormat.getNumberInstance(Locale.UK); 29 | numberFormat.setMaximumFractionDigits(340); 30 | } 31 | 32 | @Override 33 | public final String getContentType() 34 | { 35 | return "text/xml"; 36 | } 37 | 38 | @Override 39 | public final void write(OutputStream out) throws IOException 40 | { 41 | try 42 | { 43 | xml = XmlPullParserFactory.newInstance().newSerializer(); 44 | } 45 | catch(XmlPullParserException e) 46 | { 47 | throw new RuntimeException("Cannot initialize serializer", e); 48 | } 49 | xml.setOutput(out, CHARSET); 50 | xml.startDocument(CHARSET, null); 51 | 52 | write(); 53 | 54 | if(xml.getName() != null) 55 | { 56 | throw new IllegalStateException("Forgot to close a tag"); 57 | } 58 | 59 | xml.endDocument(); 60 | xml.flush(); 61 | } 62 | 63 | protected final void begin(String name) throws IOException 64 | { 65 | xml.startTag(null, name); 66 | } 67 | 68 | protected final void end() throws IOException 69 | { 70 | if(xml.getName() == null) 71 | { 72 | throw new IllegalStateException("Closed one tag to many"); 73 | } 74 | xml.endTag(null, xml.getName()); 75 | } 76 | 77 | protected final void attribute(String key, String value) throws IOException 78 | { 79 | xml.attribute(null, key, value); 80 | } 81 | 82 | protected final void attribute(String key, float value) throws IOException 83 | { 84 | xml.attribute(null, key, numberFormat.format(value)); 85 | } 86 | 87 | protected final void attribute(String key, double value) throws IOException 88 | { 89 | xml.attribute(null, key, numberFormat.format(value)); 90 | } 91 | 92 | protected final void attribute(String key, int value) throws IOException 93 | { 94 | xml.attribute(null, key, String.valueOf(value)); 95 | } 96 | 97 | protected final void attribute(String key, long value) throws IOException 98 | { 99 | xml.attribute(null, key, String.valueOf(value)); 100 | } 101 | 102 | protected final void attribute(String key, byte value) throws IOException 103 | { 104 | xml.attribute(null, key, String.valueOf(value)); 105 | } 106 | 107 | protected final void attribute(String key, boolean value) throws IOException 108 | { 109 | xml.attribute(null, key, String.valueOf(value)); 110 | } 111 | 112 | protected final void text(String text) throws IOException 113 | { 114 | xml.text(text); 115 | } 116 | 117 | protected abstract void write() throws IOException; 118 | } 119 | -------------------------------------------------------------------------------- /libs/messages/src/main/java/de/westnordost/osmapi/messages/MessagesParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.messages; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.text.ParseException; 6 | import java.time.Instant; 7 | import java.util.HashMap; 8 | import java.util.Locale; 9 | import java.util.Map; 10 | 11 | import de.westnordost.osmapi.ApiResponseReader; 12 | import de.westnordost.osmapi.common.Handler; 13 | import de.westnordost.osmapi.common.XmlParser; 14 | import de.westnordost.osmapi.user.User; 15 | 16 | /** Parses a list of OpenStreetMap messages from OSM Messages API 0.6. It parses the XML naively, 17 | * i.e. it does not care where in the XML the messages are. */ 18 | public class MessagesParser extends XmlParser implements ApiResponseReader 19 | { 20 | private static final String 21 | MESSAGE = "message", 22 | TITLE = "title", 23 | BODY = "body"; 24 | 25 | /* temporary map so we do not parse and hold many times the same user */ 26 | private Map users; 27 | 28 | private final Handler handler; 29 | 30 | private Message message = null; 31 | 32 | public MessagesParser(Handler handler) 33 | { 34 | this.handler = handler; 35 | } 36 | 37 | @Override 38 | public Void parse(InputStream in) throws IOException 39 | { 40 | users = new HashMap<>(); 41 | doParse(in); 42 | users = null; 43 | return null; 44 | } 45 | 46 | @Override 47 | protected void onStartElement() throws ParseException 48 | { 49 | String name = getName(); 50 | 51 | if (MESSAGE.equals(name)) 52 | { 53 | message = parseMessage(); 54 | } 55 | } 56 | 57 | @Override 58 | protected void onEndElement() throws ParseException 59 | { 60 | String name = getName(); 61 | 62 | if (MESSAGE.equals(name)) 63 | { 64 | handler.handle(message); 65 | message = null; 66 | } 67 | else if (TITLE.equals(name)) 68 | { 69 | message.title = getText(); 70 | } 71 | else if (BODY.equals(name)) 72 | { 73 | message.body = getText(); 74 | } 75 | } 76 | 77 | private Message parseMessage() { 78 | Message message = new Message(); 79 | message.id = getLongAttribute("id"); 80 | message.sentOn = Instant.parse(getAttribute("sent_on")); 81 | message.read = getBooleanAttribute("message_read"); 82 | message.deleted = getBooleanAttribute("deleted"); 83 | String bodyFormat = getAttribute("body_format"); 84 | if (bodyFormat == null) bodyFormat = "markdown"; 85 | message.bodyFormat = Message.BodyFormat.valueOf(bodyFormat.toUpperCase(Locale.UK)); 86 | message.fromUser = parseUser("from_user_id", "from_display_name"); 87 | message.toUser = parseUser("to_user_id", "to_display_name"); 88 | return message; 89 | } 90 | 91 | private User parseUser(String idKey, String nameKey) 92 | { 93 | Long userId = getLongAttribute(idKey); 94 | if(userId == null) 95 | return null; 96 | 97 | if(!users.containsKey(userId)) 98 | { 99 | User user = new User(userId, getAttribute(nameKey)); 100 | users.put(userId, user); 101 | return user; 102 | } 103 | return users.get(userId); 104 | } 105 | } -------------------------------------------------------------------------------- /libs/core/src/test/java/de/westnordost/osmapi/map/data/OsmLatLonTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | public class OsmLatLonTest 8 | { 9 | private static final double VALID_LAT = 51.7400243; 10 | private static final double VALID_LON = 0.2400123; 11 | 12 | @Test public void fixedE7() 13 | { 14 | OsmLatLon pos = new OsmLatLon(VALID_LAT, VALID_LON); 15 | assertEquals(VALID_LAT, pos.getLatitude(), 1e-7); 16 | assertEquals(VALID_LON, pos.getLongitude(), 1e-7); 17 | } 18 | 19 | @Test public void negativeFixedE7() 20 | { 21 | OsmLatLon pos = new OsmLatLon(-VALID_LAT, -VALID_LON); 22 | assertEquals(-VALID_LAT, pos.getLatitude(), 1e-7); 23 | assertEquals(-VALID_LON, pos.getLongitude(), 1e-7); 24 | } 25 | 26 | @Test public void parse() 27 | { 28 | OsmLatLon pos = OsmLatLon.parseLatLon(String.valueOf(VALID_LAT), String.valueOf(VALID_LON)); 29 | assertEquals(VALID_LAT, pos.getLatitude(), 1e-7); 30 | assertEquals(VALID_LON, pos.getLongitude(), 1e-7); 31 | } 32 | 33 | @Test public void zeroSomething() 34 | { 35 | OsmLatLon pos = new OsmLatLon(0.0000005,0.0000003); 36 | assertEquals(0.0000005, pos.getLatitude(), 1e-7); 37 | assertEquals(0.0000003, pos.getLongitude(), 1e-7); 38 | } 39 | 40 | @Test public void testEquals() 41 | { 42 | OsmLatLon pos1 = OsmLatLon.parseLatLon(String.valueOf(VALID_LAT), String.valueOf(VALID_LON)); 43 | OsmLatLon pos2 = new OsmLatLon(VALID_LAT, VALID_LON); 44 | assertEquals(pos1, pos2); 45 | } 46 | 47 | @Test public void equalsWithNonOsmLatLon() 48 | { 49 | LatLon pos1 = new OsmLatLon(VALID_LAT, VALID_LON); 50 | LatLon pos2 = new LatLon() 51 | { 52 | public double getLongitude() { return VALID_LON; } 53 | public double getLatitude() { return VALID_LAT; } 54 | }; 55 | assertEquals(pos1, pos2); 56 | } 57 | 58 | @Test public void equalsNull() 59 | { 60 | LatLon pos1 = new OsmLatLon(VALID_LAT, VALID_LON); 61 | assertNotEquals(null, pos1); 62 | } 63 | 64 | @Test public void equalsOtherObject() 65 | { 66 | LatLon pos1 = new OsmLatLon(VALID_LAT, VALID_LON); 67 | assertNotEquals(pos1, new Object()); 68 | } 69 | 70 | @Test public void testHashCode() 71 | { 72 | OsmLatLon pos1 = new OsmLatLon(VALID_LAT, VALID_LON); 73 | OsmLatLon pos2 = new OsmLatLon(VALID_LAT, VALID_LON); 74 | assertEquals(pos1.hashCode(), pos2.hashCode()); 75 | } 76 | 77 | @Test public void hashCodeAlgoIsNotTooSimple() 78 | { 79 | OsmLatLon pos1 = new OsmLatLon(VALID_LAT, VALID_LON); 80 | OsmLatLon pos2 = new OsmLatLon(VALID_LON, VALID_LAT); 81 | assertNotEquals(pos1.hashCode(), pos2.hashCode()); 82 | } 83 | 84 | @Test public void invalidPositiveLatitude() 85 | { 86 | assertThrows(IllegalArgumentException.class, () -> new OsmLatLon(90.0000001, 0)); 87 | } 88 | 89 | @Test public void invalidNegativeLatitude() 90 | { 91 | assertThrows(IllegalArgumentException.class, () -> new OsmLatLon(-90.0000001, 0)); 92 | } 93 | 94 | @Test public void invalidPositiveLongitude() 95 | { 96 | assertThrows(IllegalArgumentException.class, () -> new OsmLatLon(0, 180.0000001)); 97 | } 98 | 99 | @Test public void invalidNegativeLongitude() 100 | { 101 | assertThrows(IllegalArgumentException.class, () -> new OsmLatLon(0, -180.0000001)); 102 | } 103 | 104 | @Test public void invalidNegative360Longitude() 105 | { 106 | assertThrows(IllegalArgumentException.class, () -> new OsmLatLon(0, -350.0)); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /libs/user/src/test/java/de/westnordost/osmapi/user/UserDetailsParserTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.IOException; 6 | import java.time.Instant; 7 | import java.util.List; 8 | 9 | import de.westnordost.osmapi.TestUtils; 10 | import de.westnordost.osmapi.common.SingleElementHandler; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | public class UserDetailsParserTest 15 | { 16 | @Test public void optionalElements() 17 | { 18 | String xml = 19 | "" + 20 | ""; 21 | 22 | UserDetails user = parseOneDetails(xml); 23 | assertFalse(user.considersHisContributionsAsPublicDomain); 24 | assertNull(user.homeLocation); 25 | assertNull(user.homeZoom); 26 | assertNull(user.preferredLanguages); 27 | assertEquals(0,user.inboxMessageCount); 28 | assertEquals(0,user.sentMessagesCount); 29 | assertEquals(0,user.unreadMessagesCount); 30 | } 31 | 32 | @Test public void basicElements() 33 | { 34 | String xml = 35 | "" + 36 | " abc" + 37 | " " + 38 | " " + 39 | " " + 40 | " " + 41 | " " + 42 | ""; 43 | 44 | UserDetails user = parseOneDetails(xml); 45 | assertFalse(user.considersHisContributionsAsPublicDomain); 46 | assertNotNull(user.homeLocation); 47 | assertEquals(Instant.parse("2013-01-20T17:16:23Z"), user.createdAt); 48 | assertEquals(16.8151000, user.homeLocation.getLatitude(), 1e-7); 49 | assertEquals(96.1860000, user.homeLocation.getLongitude(), 1e-7); 50 | assertNotNull(user.homeZoom); 51 | assertEquals(3, (byte) user.homeZoom); 52 | } 53 | 54 | @Test public void preferredLanguages() 55 | { 56 | String xml = 57 | "" + 58 | " " + 59 | " de" + 60 | " en-US" + 61 | " en" + 62 | " " + 63 | ""; 64 | 65 | UserDetails user = parseOneDetails(xml); 66 | List langs = user.preferredLanguages; 67 | assertNotNull(langs); 68 | assertEquals(3, langs.size()); 69 | assertEquals("de", langs.get(0)); 70 | assertEquals("en-US", langs.get(1)); 71 | assertEquals("en", langs.get(2)); 72 | } 73 | 74 | @Test public void messages() 75 | { 76 | String xml = 77 | "" + 78 | " " + 79 | " " + 80 | " " + 81 | " " + 82 | ""; 83 | 84 | UserDetails user = parseOneDetails(xml); 85 | assertEquals(24, user.inboxMessageCount); 86 | assertEquals(1, user.unreadMessagesCount); 87 | assertEquals(29, user.sentMessagesCount); 88 | } 89 | 90 | 91 | private UserDetails parseOneDetails(String xml) 92 | { 93 | try 94 | { 95 | SingleElementHandler handler = new SingleElementHandler<>(); 96 | new UserDetailsParser(handler).parse(TestUtils.asInputStream(xml)); 97 | return (UserDetails) handler.get(); 98 | } 99 | catch(IOException e) 100 | { 101 | throw new RuntimeException(e); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/map/data/BoundingBox.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.io.Serializable; 4 | import java.text.NumberFormat; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Locale; 8 | 9 | /** A rectangle in latitude longitude coordinates. Bounds are immutable. */ 10 | public class BoundingBox implements Serializable 11 | { 12 | private static final long serialVersionUID = 1L; 13 | 14 | private OsmLatLon min; 15 | private OsmLatLon max; 16 | 17 | public BoundingBox(final double latMin, final double lonMin, final double latMax, final double lonMax) 18 | { 19 | this(new OsmLatLon(latMin, lonMin), new OsmLatLon(latMax, lonMax)); 20 | } 21 | 22 | public BoundingBox(LatLon min, LatLon max) 23 | { 24 | this.min = new OsmLatLon(min); 25 | this.max = new OsmLatLon(max); 26 | 27 | if(!isValid()) 28 | { 29 | throw new IllegalArgumentException("Min latitude " + min.getLatitude() + 30 | " is greater than max latitude " + max.getLatitude()); 31 | } 32 | } 33 | 34 | public LatLon getMin() 35 | { 36 | return min; 37 | } 38 | 39 | public LatLon getMax() 40 | { 41 | return max; 42 | } 43 | 44 | public double getMinLatitude() 45 | { 46 | return min.getLatitude(); 47 | } 48 | 49 | public double getMaxLatitude() 50 | { 51 | return max.getLatitude(); 52 | } 53 | 54 | public double getMinLongitude() 55 | { 56 | return min.getLongitude(); 57 | } 58 | 59 | public double getMaxLongitude() 60 | { 61 | return max.getLongitude(); 62 | } 63 | 64 | 65 | 66 | public String getAsLeftBottomRightTopString() 67 | { 68 | NumberFormat df = NumberFormat.getNumberInstance(Locale.UK); 69 | df.setMaximumFractionDigits(340); 70 | 71 | return df.format(getMinLongitude()) + "," + df.format(getMinLatitude()) + "," + 72 | df.format(getMaxLongitude()) + "," + df.format(getMaxLatitude()); 73 | } 74 | 75 | public boolean crosses180thMeridian() 76 | { 77 | return min.getLongitude() > max.getLongitude(); 78 | } 79 | 80 | private boolean isValid() 81 | { 82 | return min.getLatitude() <= max.getLatitude(); 83 | } 84 | 85 | /** @return two new bounds split alongside the 180th meridian or, if these bounds do not cross 86 | * the 180th meridian, just this object in a list */ 87 | public List splitAt180thMeridian() 88 | { 89 | if(crosses180thMeridian()) 90 | { 91 | return Arrays.asList( 92 | new BoundingBox( min, new OsmLatLon(max.getLatitude(), LatLons.MAX_VALUE.getLongitude()) ), 93 | new BoundingBox( new OsmLatLon(min.getLatitude(), LatLons.MIN_VALUE.getLongitude()), max ) 94 | ); 95 | } 96 | 97 | return Arrays.asList(this); 98 | } 99 | 100 | @Override 101 | public boolean equals(Object other) 102 | { 103 | if(other == this) return true; 104 | if(other == null || !(other instanceof BoundingBox)) return false; 105 | 106 | // we do not rely on that every implementation of LatLon implements equals() properly 107 | BoundingBox otherBounds = (BoundingBox) other; 108 | return otherBounds.getMinLatitude() == getMinLatitude() 109 | && otherBounds.getMaxLatitude() == getMaxLatitude() 110 | && otherBounds.getMinLongitude() == getMinLongitude() 111 | && otherBounds.getMaxLongitude() == getMaxLongitude(); 112 | } 113 | 114 | @Override 115 | public int hashCode() 116 | { 117 | double[] allThemDoubles = new double[] 118 | { 119 | getMinLatitude(), getMaxLatitude(), getMaxLatitude(), getMaxLongitude() 120 | }; 121 | 122 | return Arrays.hashCode(allThemDoubles); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/capabilities/CapabilitiesParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.capabilities; 2 | 3 | 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import de.westnordost.osmapi.ApiResponseReader; 10 | import de.westnordost.osmapi.common.XmlParser; 11 | 12 | /** Parses the osm server capabilites and limits (API 0.6). It parses the XML naively, i.e. it 13 | * does not care where in the XML the notes nodes are. */ 14 | public class CapabilitiesParser extends XmlParser implements ApiResponseReader 15 | { 16 | private static final String API = "api"; 17 | private static final String POLICY = "policy"; 18 | private static final String IMAGERY = "imagery"; 19 | 20 | private Capabilities capabilities; 21 | private List imageBlacklistRegexes; 22 | 23 | @Override 24 | public Capabilities parse(InputStream in) throws IOException 25 | { 26 | capabilities = new Capabilities(); 27 | doParse(in); 28 | return capabilities; 29 | } 30 | 31 | @Override 32 | protected void onStartElement() 33 | { 34 | if(API.equals(getParentName())) 35 | { 36 | parseApiElement(); 37 | } 38 | else if(POLICY.equals(getParentName())) 39 | { 40 | if(IMAGERY.equals(getName())) 41 | { 42 | imageBlacklistRegexes = new ArrayList<>(); 43 | } 44 | } 45 | else if(IMAGERY.equals(getParentName())) 46 | { 47 | if("blacklist".equals(getName())) 48 | { 49 | imageBlacklistRegexes.add(getAttribute("regex")); 50 | } 51 | } 52 | } 53 | 54 | private void parseApiElement() 55 | { 56 | String name = getName(); 57 | switch (name) 58 | { 59 | case "version": 60 | capabilities.minSupportedApiVersion = getFloatAttribute("minimum"); 61 | capabilities.maxSupportedApiVersion = getFloatAttribute("maximum"); 62 | break; 63 | case "area": 64 | capabilities.maxMapQueryAreaInSquareDegrees = getFloatAttribute("maximum"); 65 | break; 66 | case "note_area": 67 | capabilities.maxNotesQueryAreaInSquareDegrees = getFloatAttribute("maximum"); 68 | break; 69 | case "tracepoints": 70 | capabilities.maxPointsInGpsTracePerPage = getIntAttribute("per_page"); 71 | break; 72 | case "waynodes": 73 | capabilities.maxNodesInWay = getIntAttribute("maximum"); 74 | break; 75 | case "relationmembers": 76 | capabilities.maxMembersInRelation = getIntAttribute("maximum"); 77 | break; 78 | case "changesets": 79 | capabilities.maxElementsPerChangeset = getIntAttribute("maximum_elements"); 80 | capabilities.defaultChangesetsQueryLimit = getIntAttribute("default_query_limit"); 81 | capabilities.maximumChangesetsQueryLimit = getIntAttribute("maximum_query_limit"); 82 | break; 83 | case "timeout": 84 | capabilities.timeoutInSeconds = getIntAttribute("seconds"); 85 | break; 86 | case "status": 87 | capabilities.databaseStatus = Capabilities.parseApiStatus(getAttribute("database")); 88 | capabilities.mapDataStatus = Capabilities.parseApiStatus(getAttribute("api")); 89 | capabilities.gpsTracesStatus = Capabilities.parseApiStatus(getAttribute("gpx")); 90 | break; 91 | case "notes": 92 | capabilities.defaultNotesQueryLimit = getIntAttribute("default_query_limit"); 93 | capabilities.maximumNotesQueryLimit = getIntAttribute("maximum_query_limit"); 94 | } 95 | } 96 | 97 | @Override 98 | protected void onEndElement() 99 | { 100 | if(POLICY.equals(getParentName()) && IMAGERY.equals(getName())) 101 | { 102 | capabilities.imageryBlacklistRegExes = imageBlacklistRegexes; 103 | imageBlacklistRegexes = null; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Combine_With_Data_Processing_Libraries.md: -------------------------------------------------------------------------------- 1 | # Combine With Data Processing Libraries 2 | 3 | This library's core function is to facilitate communication with the Osm Api. For advanced data processing (i.e. converting between various data formats), there are some fully featured map data processing libraries like [Osmosis](https://github.com/openstreetmap/osmosis) or [osm4j](http://www.topobyte.de/projects/osm4j/) around. These libraries each use their own data structures, so how to best plug this library together with such a library? 4 | 5 | There are several injection points where you can make the data your own: 6 | 7 | ## Option 1: Create own MapDataFactory 8 | If you have control over your map data classes, you can make them implement `Node`, `Way` and `Relation` or (if not) write a decorator/wrapper that implements those interfaces. Then, you simply implement a custom [MapDataFactory](https://github.com/westnordost/osmapi/blob/master/libs/map/src/main/java/de/westnordost/osmapi/map/MapDataFactory.java) and pass it to the MapDataDao / MapDataHistoryDao. 9 | 10 | new MapDataApi(osm, new MyMapDataFactory()); 11 | 12 | ## Option 2: Wrap the MapDataHandler 13 | If you want to avoid adding wrappers for the data classes, you can instead copy the osmapi data in a wrapper around [MapDataHandler](https://github.com/westnordost/osmapi/blob/master/libs/map/src/main/java/de/westnordost/osmapi/map/handler/MapDataHandler.java) into your data. If you intend to upload changes using osmapi functionality, you need to convert the data back on upload of course. I.e. 14 | 15 | // MyMapDataHandlerWrapper implements MapDataHandler and creates my data from osmapi data 16 | new MapDataApi(osm).getMap(bounds, new MyMapDataHandlerWrapper(sink)); 17 | 18 | With this option, osmapi data structures only serve as simple data transfer objects that are created and discarded during parsing and writing. 19 | 20 | ## Option 3: Write own Dao using OsmConnection 21 | Osmosis can parse map data and also write map data _changes_ (few libraries can do that) itself, so in the case of Osmosis it can make sense to simply pass the InputStream/OutputStream to the library and leave the xml parsing and writing to itself. osmapi can still facilitate this a bit by letting the [OsmConnection](https://github.com/westnordost/osmapi/blob/master/libs/core/src/main/java/de/westnordost/osmapi/OsmConnection.java) manage the connection to the Osm Api 22 | 23 | // in OsmosisMapDataDao.java 24 | public void getMap(BoundingBox bounds, Sink sink) 25 | { 26 | osm.makeRequest("map?bbox=" + bounds.getAsLeftBottomRightTopString(), 27 | new ApiResponseReader() 28 | { 29 | Void parse(InputStream in) 30 | { 31 | SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); 32 | parser.parse(in, new OsmHandler(sink, true)); 33 | return null; 34 | } 35 | } 36 | } 37 | 38 | ...and something similar for the upload. Note that Osmosis does not read the diff response on uploading map data changes to the server. 39 | 40 | ## Option 4: Don't use this library for map data 41 | 42 | Now, OsmConnection alone does not do _that_ much, mostly streamlining error handling and managing the URL connection. 43 | 44 | So in case your data processing library offers everything that osmapi offers in that aspect, it is your choice whether to leave everything regarding map data to that library and only use the osmapi for other Api Calls. 45 | 46 | Osmosis has the [XmlDownloader](https://github.com/openstreetmap/osmosis/blob/master/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloader.java) and [XmlChangeUploader](https://github.com/openstreetmap/osmosis/blob/master/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploader.java). Again, it is to note that XmlChangeUploader does not read the diff response from the server, i.e. does not enable you to update your data model. 47 | -------------------------------------------------------------------------------- /libs/core/src/test/java/de/westnordost/osmapi/common/XmlWriterTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.IOException; 7 | 8 | import de.westnordost.osmapi.TestUtils; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | public class XmlWriterTest 13 | { 14 | private static final String xmlBlob = ""; 15 | 16 | @Test public void didNotCloseTag() 17 | { 18 | assertThrows( 19 | IllegalStateException.class, 20 | () -> new TestXmlWriter() 21 | { 22 | @Override 23 | protected void write() throws IOException 24 | { 25 | begin("test"); 26 | } 27 | }.test() 28 | ); 29 | } 30 | 31 | @Test public void didCloseOneTagTooMany() 32 | { 33 | assertThrows( 34 | IllegalStateException.class, 35 | () -> new TestXmlWriter() { 36 | @Override protected void write() throws IOException 37 | { 38 | begin("test"); 39 | end(); 40 | end(); 41 | } 42 | }.test() 43 | ); 44 | } 45 | 46 | @Test public void simple() throws IOException 47 | { 48 | String result = new TestXmlWriter() 49 | { 50 | @Override 51 | protected void write() throws IOException 52 | { 53 | begin("test"); 54 | end(); 55 | } 56 | }.test(); 57 | 58 | assertEquals(xmlBlob + "", result); 59 | } 60 | 61 | @Test public void text() throws IOException 62 | { 63 | String result = new TestXmlWriter() 64 | { 65 | @Override 66 | protected void write() throws IOException 67 | { 68 | begin("test"); 69 | text("jo <>"); 70 | end(); 71 | } 72 | }.test(); 73 | 74 | assertEquals(xmlBlob + "jo <>", result); 75 | } 76 | 77 | 78 | @Test public void attribute() throws IOException 79 | { 80 | String result = new TestXmlWriter() 81 | { 82 | @Override 83 | protected void write() throws IOException 84 | { 85 | begin("test"); 86 | attribute("key", "value"); 87 | end(); 88 | } 89 | }.test(); 90 | 91 | assertEquals(xmlBlob + "", result); 92 | } 93 | 94 | @Test public void doubleAttributeIsNotInScientificNotation() throws IOException 95 | { 96 | String result = new TestXmlWriter() 97 | { 98 | @Override 99 | protected void write() throws IOException 100 | { 101 | begin("test"); 102 | attribute("key", 0.0000001); 103 | end(); 104 | } 105 | }.test(); 106 | 107 | assertEquals(xmlBlob + "", result); 108 | } 109 | 110 | @Test public void nested() throws IOException 111 | { 112 | String result = new TestXmlWriter() 113 | { 114 | @Override 115 | protected void write() throws IOException 116 | { 117 | begin("test"); 118 | begin("a"); 119 | end(); 120 | end(); 121 | } 122 | }.test(); 123 | 124 | assertEquals(xmlBlob + "", result); 125 | } 126 | 127 | private static abstract class TestXmlWriter extends XmlWriter 128 | { 129 | public String test() throws IOException 130 | { 131 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 132 | write(os); 133 | String result = TestUtils.asString(os); 134 | // not interested in indentation 135 | result = result.replaceAll("(?m)^[\\s]*", ""); 136 | // not interested in tabs and newlines 137 | result = result.replaceAll("[\r\n\t]",""); 138 | // " and ' does not make a difference 139 | result = result.replaceAll("\"","'"); 140 | 141 | return result; 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /libs/notes/src/main/java/de/westnordost/osmapi/notes/NotesParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.notes; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.text.ParseException; 6 | import java.util.HashMap; 7 | import java.util.Locale; 8 | import java.util.Map; 9 | 10 | import de.westnordost.osmapi.ApiResponseReader; 11 | import de.westnordost.osmapi.common.Handler; 12 | import de.westnordost.osmapi.common.XmlParser; 13 | import de.westnordost.osmapi.map.data.OsmLatLon; 14 | import de.westnordost.osmapi.user.User; 15 | 16 | /** Parses a list of openstreetmap notes from OSM Notes API 0.6. It parses the XML naively, i.e. it 17 | * does not care where in the XML the notes nodes are. */ 18 | public class NotesParser extends XmlParser implements ApiResponseReader 19 | { 20 | private static final String 21 | NOTE = "note", 22 | COMMENT = "comment"; 23 | 24 | private static final NotesDateFormat FORMATTER = new NotesDateFormat(); 25 | 26 | /* temporary map so we do not parse and hold many times the same user */ 27 | private Map users; 28 | 29 | private final Handler handler; 30 | private Note currentNote; 31 | private NoteComment currentComment; 32 | 33 | private long userId = -1; 34 | private String userName; 35 | 36 | public NotesParser(Handler handler) 37 | { 38 | this.handler = handler; 39 | } 40 | 41 | @Override 42 | public Void parse(InputStream in) throws IOException 43 | { 44 | users = new HashMap<>(); 45 | doParse(in); 46 | users = null; 47 | return null; 48 | } 49 | 50 | @Override 51 | protected void onStartElement() 52 | { 53 | String name = getName(); 54 | 55 | if (name.equals(NOTE)) 56 | { 57 | currentNote = new Note(); 58 | currentNote.position = OsmLatLon.parseLatLon(getAttribute("lat"), getAttribute("lon")); 59 | } 60 | else if(name.equals(COMMENT)) 61 | { 62 | currentComment = new NoteComment(); 63 | } 64 | } 65 | 66 | @Override 67 | protected void onEndElement() throws ParseException 68 | { 69 | String name = getName(); 70 | String parentName = getParentName(); 71 | String txt = getText(); 72 | 73 | if(NOTE.equals(name)) 74 | { 75 | handler.handle( currentNote ); 76 | currentNote = null; 77 | } 78 | else if(COMMENT.equals(name)) 79 | { 80 | if(userId != -1 || userName != null) 81 | { 82 | if(!users.containsKey(userId)) 83 | { 84 | users.put(userId, new User(userId, userName)); 85 | } 86 | 87 | currentComment.user = users.get(userId); 88 | 89 | userId = -1; 90 | userName = null; 91 | } 92 | currentNote.comments.add(currentComment); 93 | currentComment = null; 94 | } 95 | else if(NOTE.equals(parentName)) 96 | { 97 | parseNoteTextNode(name,txt); 98 | } 99 | else if(COMMENT.equals(parentName)) 100 | { 101 | parseCommentTextNode(name,txt); 102 | } 103 | } 104 | 105 | private void parseNoteTextNode(String name, String txt) throws ParseException 106 | { 107 | switch (name) 108 | { 109 | case "id": 110 | currentNote.id = Long.parseLong(txt); 111 | break; 112 | case "status": 113 | currentNote.status = Note.Status.valueOf(txt.toUpperCase(Locale.UK)); 114 | break; 115 | case "date_created": 116 | currentNote.createdAt = FORMATTER.parse(txt); 117 | break; 118 | case "date_closed": 119 | currentNote.closedAt = FORMATTER.parse(txt); 120 | break; 121 | } 122 | } 123 | 124 | private void parseCommentTextNode(String name, String txt) throws ParseException 125 | { 126 | switch (name) 127 | { 128 | case "date": 129 | currentComment.date = FORMATTER.parse(txt); 130 | break; 131 | case "user": 132 | userName = txt; 133 | break; 134 | case "uid": 135 | userId = Long.parseLong(txt); 136 | break; 137 | case "text": 138 | currentComment.text = txt; 139 | break; 140 | case "action": 141 | currentComment.action = NoteComment.Action.valueOf(txt.toUpperCase(Locale.UK)); 142 | break; 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /libs/traces/src/test/java/de/westnordost/osmapi/traces/GpxTrackWriterTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.IOException; 7 | import java.time.Instant; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import de.westnordost.osmapi.TestUtils; 12 | import de.westnordost.osmapi.common.ListHandler; 13 | import de.westnordost.osmapi.map.data.OsmLatLon; 14 | 15 | import static org.junit.Assert.*; 16 | 17 | public class GpxTrackWriterTest 18 | { 19 | @Test public void empty() throws IOException 20 | { 21 | List elements = new ArrayList<>(); 22 | checkElementsEqual(elements, writeAndRead(elements)); 23 | } 24 | 25 | @Test public void minimal() throws IOException 26 | { 27 | List elements = new ArrayList<>(); 28 | elements.add(new GpsTrackpoint( 29 | new OsmLatLon(1.234, 2.345), 30 | Instant.now() 31 | )); 32 | 33 | checkElementsEqual(elements, writeAndRead(elements)); 34 | } 35 | 36 | @Test public void props() throws IOException 37 | { 38 | List elements = new ArrayList<>(); 39 | elements.add(new GpsTrackpoint( 40 | new OsmLatLon(1.234, 2.345), 41 | Instant.now(), 42 | false, 43 | 42.1f, 44 | 789.1f 45 | )); 46 | 47 | checkElementsEqual(elements, writeAndRead(elements)); 48 | } 49 | 50 | @Test public void oneSegment() throws IOException 51 | { 52 | List elements = new ArrayList<>(); 53 | elements.add( new GpsTrackpoint(new OsmLatLon(1.234, 2.345), Instant.now())); 54 | elements.add( new GpsTrackpoint(new OsmLatLon(2.234, 3.567), Instant.now())); 55 | 56 | checkElementsEqual(elements, writeAndRead(elements)); 57 | } 58 | 59 | @Test public void twoSegments() throws IOException 60 | { 61 | List elements = new ArrayList<>(); 62 | elements.add( new GpsTrackpoint(new OsmLatLon(1.234, 2.345), Instant.now())); 63 | elements.add( new GpsTrackpoint(new OsmLatLon(2.234, 3.567), Instant.now())); 64 | 65 | GpsTrackpoint newSegmentPoint = new GpsTrackpoint(new OsmLatLon(3.234, 4.567), Instant.now(), true, null, null); 66 | 67 | elements.add( newSegmentPoint); 68 | 69 | checkElementsEqual(elements, writeAndRead(elements)); 70 | } 71 | 72 | @Test public void roundFloatsToOneDecimal() throws IOException 73 | { 74 | List elements = new ArrayList<>(); 75 | elements.add(new GpsTrackpoint( 76 | new OsmLatLon(1.234, 2.345), 77 | Instant.now(), 78 | false, 79 | 42.191655223465f, 80 | 789.113524654646f 81 | )); 82 | 83 | GpsTrackpoint pointNew = writeAndRead(elements).get(0); 84 | assertEquals(789.1f, pointNew.elevation, 1e-7); 85 | assertEquals(42.2f, pointNew.horizontalDilutionOfPrecision, 1e-7); 86 | } 87 | 88 | private void checkElementsEqual(List expect, List actual) 89 | { 90 | assertEquals(expect.size(), actual.size()); 91 | for(int i = 0; i 0) 102 | assertEquals(e.isFirstPointInTrackSegment, a.isFirstPointInTrackSegment); 103 | else 104 | assertTrue(a.isFirstPointInTrackSegment); 105 | } 106 | } 107 | 108 | private List writeAndRead(Iterable elements) 109 | throws IOException 110 | { 111 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 112 | new GpxTrackWriter("user agent test", elements).write(out); 113 | String xml = TestUtils.asString(out); 114 | ListHandler handler = new ListHandler<>(); 115 | new GpxTrackParser(handler).parse(TestUtils.asInputStream(xml)); 116 | return handler.get(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /libs/traces/src/test/java/de/westnordost/osmapi/traces/GpsTracesParserTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.traces; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.IOException; 6 | import java.time.Instant; 7 | 8 | import de.westnordost.osmapi.TestUtils; 9 | import de.westnordost.osmapi.common.Handler; 10 | import de.westnordost.osmapi.common.SingleElementHandler; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | public class GpsTracesParserTest 15 | { 16 | @Test public void fields() 17 | { 18 | String xml = 19 | "" + 20 | "" + 21 | "" + 22 | ""+ 23 | ""; 24 | 25 | GpsTraceDetails result = parseOne(xml); 26 | 27 | assertEquals(123, result.id); 28 | assertEquals("näim", result.name); 29 | assertEquals(51.68812, result.position.getLatitude(), 1e-7); 30 | assertEquals(-3.0294167, result.position.getLongitude(), 1e-7); 31 | assertEquals("testo", result.userName); 32 | assertEquals(false, result.pending); 33 | assertEquals(null, result.tags); 34 | assertEquals(null, result.description); 35 | assertEquals(null, result.createdAt); 36 | } 37 | 38 | @Test public void date() 39 | { 40 | String xml = 41 | "" + 42 | "" + 43 | ""+ 44 | ""; 45 | 46 | GpsTraceDetails result = parseOne(xml); 47 | assertEquals(Instant.parse("2006-12-12T15:20:32Z"), result.createdAt); 48 | } 49 | 50 | @Test public void parseVisibility() throws IOException 51 | { 52 | String xml = 53 | "" + 54 | "" + 55 | ""+ 56 | "" + 57 | ""+ 58 | "" + 59 | ""+ 60 | "" + 61 | ""+ 62 | ""; 63 | 64 | Handler handler = new Handler() 65 | { 66 | @Override 67 | public void handle(GpsTraceDetails trace) { 68 | assertEquals(getVisibility((int) trace.id), trace.visibility); 69 | } 70 | 71 | private GpsTraceDetails.Visibility getVisibility(int id) 72 | { 73 | switch(id) 74 | { 75 | case 1: return GpsTraceDetails.Visibility.PUBLIC; 76 | case 2: return GpsTraceDetails.Visibility.PRIVATE; 77 | case 3: return GpsTraceDetails.Visibility.IDENTIFIABLE; 78 | case 4: return GpsTraceDetails.Visibility.TRACKABLE; 79 | } 80 | return null; 81 | } 82 | }; 83 | new GpsTracesParser(handler).parse(TestUtils.asInputStream(xml)); 84 | } 85 | 86 | @Test public void tags() 87 | { 88 | String xml = 89 | "" + 90 | "" + 91 | "yoyoyo" + 92 | "hihihi" + 93 | ""+ 94 | ""; 95 | 96 | GpsTraceDetails result = parseOne(xml); 97 | 98 | assertEquals(2,result.tags.size()); 99 | assertEquals("yoyoyo",result.tags.get(0)); 100 | assertEquals("hihihi",result.tags.get(1)); 101 | } 102 | 103 | @Test public void description() 104 | { 105 | String xml = 106 | "" + 107 | "" + 108 | "hiho" + 109 | ""+ 110 | ""; 111 | 112 | GpsTraceDetails result = parseOne(xml); 113 | 114 | assertEquals("hiho",result.description); 115 | } 116 | 117 | private GpsTraceDetails parseOne(String xml) 118 | { 119 | try 120 | { 121 | SingleElementHandler handler = new SingleElementHandler<>(); 122 | new GpsTracesParser(handler).parse(TestUtils.asInputStream(xml)); 123 | return handler.get(); 124 | } 125 | catch(IOException e) 126 | { 127 | throw new RuntimeException(e); 128 | } 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /libs/core/src/test/java/de/westnordost/osmapi/common/FormDataWriterTest.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.IOException; 7 | import java.util.Arrays; 8 | import java.util.Collection; 9 | import java.util.Collections; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | import de.westnordost.osmapi.ApiRequestWriter; 14 | import de.westnordost.osmapi.TestUtils; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | 18 | public class FormDataWriterTest 19 | { 20 | @Test public void oneField() throws IOException 21 | { 22 | Map params = new HashMap<>(); 23 | params.put("TestXY", "one two three"); 24 | checkOutput(params); 25 | } 26 | 27 | @Test public void multipleField() throws IOException 28 | { 29 | Map params = new HashMap<>(); 30 | params.put("TestXY", "one two three"); 31 | params.put("TestAB", "one two three four"); 32 | checkOutput(params); 33 | } 34 | 35 | @Test public void fileField() throws IOException 36 | { 37 | Map params = new HashMap<>(); 38 | params.put("TestXY", "one two three"); 39 | FileFieldInfo info = new FileFieldInfo(); 40 | info.fileName = "failname"; 41 | info.name = "naim"; 42 | info.subWriter = new PlainTextWriter("my daita"); 43 | checkOutput(params, Arrays.asList(info)); 44 | } 45 | 46 | @Test public void multipleFileField() throws IOException 47 | { 48 | Map params = new HashMap<>(); 49 | params.put("TestXY", "one two three"); 50 | FileFieldInfo info = new FileFieldInfo(); 51 | info.fileName = "failname"; 52 | info.name = "naim"; 53 | info.subWriter = new PlainTextWriter("my daita"); 54 | FileFieldInfo info2 = new FileFieldInfo(); 55 | info2.fileName = "failname2"; 56 | info2.name = "naim2"; 57 | info2.subWriter = new PlainTextWriter("my daita2"); 58 | checkOutput(params, Arrays.asList(info, info2)); 59 | } 60 | 61 | private void checkOutput(final Map params) throws IOException 62 | { 63 | checkOutput(params, Collections. emptyList()); 64 | } 65 | 66 | private void checkOutput( 67 | final Map params, 68 | final Collection fileParams) throws IOException 69 | { 70 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 71 | FormDataWriter writer = new FormDataWriter() 72 | { 73 | @Override 74 | protected void write() throws IOException 75 | { 76 | for(Map.Entry entry : params.entrySet()) 77 | { 78 | addField(entry.getKey(), entry.getValue()); 79 | } 80 | for(FileFieldInfo info : fileParams) 81 | { 82 | addFileField(info.name, info.fileName, info.subWriter); 83 | } 84 | } 85 | }; 86 | writer.write(out); 87 | 88 | String expected = createExpectedOutput(writer.boundary, params, fileParams); 89 | assertEquals(expected, TestUtils.asString(out)); 90 | } 91 | 92 | private String createExpectedOutput(String boundary, Map params, 93 | Collection fileParams) throws IOException 94 | { 95 | // lets reimplement the whole FormDataWriter ;-) 96 | 97 | StringBuilder expected = new StringBuilder(); 98 | for(Map.Entry entry : params.entrySet()) 99 | { 100 | expected.append( 101 | "--" + boundary + "\r\n" + 102 | "Content-Disposition: form-data; name=\""+entry.getKey()+"\"\r\n" + 103 | "Content-Type: text/plain; charset=utf-8\r\n\r\n" + 104 | entry.getValue()+"\r\n"); 105 | } 106 | for(FileFieldInfo info : fileParams) 107 | { 108 | expected.append( 109 | "--" + boundary + "\r\n" + 110 | "Content-Disposition: form-data; name=\""+info.name+"\"; " + 111 | "filename=\"" + info.fileName + "\"\r\n" + 112 | "Content-Type: "+info.subWriter.getContentType()+"\r\n\r\n"); 113 | ByteArrayOutputStream subOut = new ByteArrayOutputStream(); 114 | info.subWriter.write(subOut); 115 | expected.append(TestUtils.asString(subOut)); 116 | expected.append("\r\n"); 117 | } 118 | 119 | expected.append("--" + boundary + "--\r\n"); 120 | return expected.toString(); 121 | } 122 | 123 | private class FileFieldInfo 124 | { 125 | String name; 126 | String fileName; 127 | ApiRequestWriter subWriter; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /libs/core/src/main/java/de/westnordost/osmapi/common/XmlParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.common; 2 | 3 | import org.xmlpull.v1.XmlPullParser; 4 | import org.xmlpull.v1.XmlPullParserFactory; 5 | 6 | import de.westnordost.osmapi.common.errors.XmlParserException; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.util.Stack; 11 | 12 | /** A simple XML parser that can be used quite similarly to the SAX parser but is based on the pull 13 | * parser. It has two convenience methods. You can get the parent element via getParentName and you 14 | * can get the text in the current node (in onEndElement) via getText. 15 | * 16 | * It is not very generic but generic enough for the purpose of this project */ 17 | public abstract class XmlParser 18 | { 19 | private static final String CHARSET = "UTF-8"; 20 | 21 | private Stack parentElements = new Stack<>(); 22 | private String text; 23 | private XmlPullParser xpp; 24 | 25 | protected final void doParse(InputStream in) throws XmlParserException, IOException 26 | { 27 | try 28 | { 29 | if(xpp == null) 30 | { 31 | xpp = XmlPullParserFactory.newInstance().newPullParser(); 32 | } 33 | xpp.setInput(in, CHARSET); 34 | int eventType = xpp.getEventType(); 35 | while (eventType != XmlPullParser.END_DOCUMENT) 36 | { 37 | switch (eventType) 38 | { 39 | case XmlPullParser.START_TAG: 40 | text = null; 41 | onStartElement(); 42 | parentElements.push(xpp.getName()); 43 | break; 44 | case XmlPullParser.TEXT: 45 | onTextNode(xpp.getText()); 46 | break; 47 | case XmlPullParser.END_TAG: 48 | parentElements.pop(); 49 | onEndElement(); 50 | text = null; 51 | break; 52 | } 53 | eventType = xpp.next(); 54 | } 55 | } 56 | catch(IOException e) 57 | { 58 | throw e; 59 | } 60 | catch (Exception e) 61 | { 62 | if(xpp != null) 63 | { 64 | throw new XmlParserException(xpp.getPositionDescription(), e); 65 | } 66 | else 67 | { 68 | throw new XmlParserException(e); 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * @return the name of the current element 75 | */ 76 | protected String getName() 77 | { 78 | return xpp.getName(); 79 | } 80 | 81 | /** 82 | * @param name of the attribute 83 | * @return the value of the attribute of the current element or null if it does not exist. Only non-null within 84 | * onStartElement 85 | */ 86 | protected String getAttribute(String name) 87 | { 88 | return xpp.getAttributeValue(null, name); 89 | } 90 | 91 | protected Float getFloatAttribute(String name) 92 | { 93 | String attr = getAttribute(name); 94 | return attr != null ? Float.parseFloat(attr) : null; 95 | } 96 | 97 | protected Double getDoubleAttribute(String name) 98 | { 99 | String attr = getAttribute(name); 100 | return attr != null ? Double.parseDouble(attr) : null; 101 | } 102 | 103 | protected Integer getIntAttribute(String name) 104 | { 105 | String attr = getAttribute(name); 106 | return attr != null ? Integer.parseInt(attr) : null; 107 | } 108 | 109 | protected Long getLongAttribute(String name) 110 | { 111 | String attr = getAttribute(name); 112 | return attr != null ? Long.parseLong(attr) : null; 113 | } 114 | 115 | protected Boolean getBooleanAttribute(String name) 116 | { 117 | String attr = getAttribute(name); 118 | return attr != null ? Boolean.parseBoolean(attr) : null; 119 | } 120 | 121 | protected Byte getByteAttribute(String name) 122 | { 123 | String attr = getAttribute(name); 124 | return attr != null ? Byte.parseByte(attr) : null; 125 | } 126 | 127 | /** 128 | * @return the name of the element parent to the current one or null if there is none 129 | */ 130 | protected String getParentName() 131 | { 132 | if(parentElements.empty()) return null; 133 | return parentElements.peek(); 134 | } 135 | 136 | /** 137 | * @return the last text node that was encountered in the current element. Only returns anything 138 | * else than null in onEndElement. 139 | */ 140 | protected String getText() 141 | { 142 | return text; 143 | } 144 | 145 | protected void onTextNode(String text) 146 | { 147 | this.text = text; 148 | } 149 | 150 | protected abstract void onStartElement() throws Exception; 151 | 152 | protected abstract void onEndElement() throws Exception; 153 | 154 | } 155 | -------------------------------------------------------------------------------- /libs/map/src/main/java/de/westnordost/osmapi/map/data/ModificationAwareMap.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.map.data; 2 | 3 | import java.io.Serializable; 4 | import java.util.AbstractCollection; 5 | import java.util.AbstractSet; 6 | import java.util.Collection; 7 | import java.util.Iterator; 8 | import java.util.Map; 9 | import java.util.Set; 10 | 11 | /** A Map that registers if it has been changed from its original state. */ 12 | public class ModificationAwareMap implements Map, Serializable 13 | { 14 | private static final long serialVersionUID = 1L; 15 | 16 | private final Map map; 17 | private boolean modified; 18 | 19 | public ModificationAwareMap(Map map) 20 | { 21 | this.map = map; 22 | } 23 | 24 | /** @return Whether the map has been modified. Note that this will always return true once the 25 | * map has once been modified, regardless of whether it has been restored to its 26 | * original state later on. */ 27 | public boolean isModified() 28 | { 29 | return modified; 30 | } 31 | 32 | private void onModification() 33 | { 34 | modified = true; 35 | } 36 | 37 | /* Everything below this comment: implementation of the map interface */ 38 | 39 | @Override 40 | public void clear() 41 | { 42 | map.clear(); 43 | onModification(); 44 | } 45 | 46 | @Override 47 | public boolean containsKey(Object key) 48 | { 49 | return map.containsKey(key); 50 | } 51 | 52 | @Override 53 | public boolean containsValue(Object value) 54 | { 55 | return map.containsValue(value); 56 | } 57 | 58 | @Override 59 | public Set> entrySet() 60 | { 61 | return new SetWrapper<>(map.entrySet()); 62 | } 63 | 64 | @Override 65 | public V get(Object key) 66 | { 67 | return map.get(key); 68 | } 69 | 70 | @Override 71 | public boolean isEmpty() 72 | { 73 | return map.isEmpty(); 74 | } 75 | 76 | @Override 77 | public Set keySet() 78 | { 79 | return new SetWrapper<>(map.keySet()); 80 | } 81 | 82 | @Override 83 | public V put(K key, V value) 84 | { 85 | V result = map.put(key, value); 86 | if(result != value) onModification(); 87 | return result; 88 | } 89 | 90 | @Override 91 | public void putAll(Map map) 92 | { 93 | this.map.putAll(map); 94 | if(!map.isEmpty()) onModification(); 95 | } 96 | 97 | @Override 98 | public V remove(Object key) 99 | { 100 | V result = map.remove(key); 101 | if(result != null) onModification(); 102 | return result; 103 | } 104 | 105 | @Override 106 | public int size() 107 | { 108 | return map.size(); 109 | } 110 | 111 | @Override 112 | public boolean equals(Object other) 113 | { 114 | return map.equals(other); 115 | } 116 | 117 | @Override 118 | public Collection values() 119 | { 120 | return new ValuesWrapper(map.values()); 121 | } 122 | 123 | @Override 124 | public int hashCode() 125 | { 126 | return map.hashCode(); 127 | } 128 | 129 | private class SetWrapper extends AbstractSet 130 | { 131 | private Set set; 132 | public SetWrapper(Set set) { this.set = set; } 133 | 134 | public Iterator iterator() { return new IteratorWrapper<>(set.iterator()); } 135 | public boolean contains(Object o) { return set.contains(o); } 136 | public int size() { return ModificationAwareMap.this.size(); } 137 | public void clear() { ModificationAwareMap.this.clear(); } 138 | public boolean remove(Object o) 139 | { 140 | boolean result = set.remove(o); 141 | if(result) onModification(); 142 | return result; 143 | } 144 | } 145 | 146 | private class ValuesWrapper extends AbstractCollection 147 | { 148 | private Collection values; 149 | public ValuesWrapper(Collection values) { this.values = values; } 150 | 151 | public Iterator iterator() { return new IteratorWrapper<>(values.iterator()); } 152 | public int size() { return ModificationAwareMap.this.size(); } 153 | public boolean contains(Object o) { return containsValue(o); } 154 | public void clear() { ModificationAwareMap.this.clear(); } 155 | } 156 | 157 | private class IteratorWrapper implements Iterator 158 | { 159 | private Iterator it; 160 | public IteratorWrapper(Iterator it) { this.it = it; } 161 | 162 | public boolean hasNext() { return it.hasNext(); } 163 | public E next() { return it.next(); } 164 | public void remove() 165 | { 166 | it.remove(); 167 | onModification(); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /libs/changesets/src/main/java/de/westnordost/osmapi/changesets/ChangesetParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.changesets; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.text.ParseException; 6 | import java.time.Instant; 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import de.westnordost.osmapi.ApiResponseReader; 13 | import de.westnordost.osmapi.common.Handler; 14 | import de.westnordost.osmapi.common.XmlParser; 15 | import de.westnordost.osmapi.map.data.BoundingBox; 16 | import de.westnordost.osmapi.map.data.OsmLatLon; 17 | import de.westnordost.osmapi.user.User; 18 | 19 | /** 20 | * Parses changeset infos. It parses the XML naively, i.e. it does not care 21 | * where in the XML the notes nodes are. 22 | */ 23 | public class ChangesetParser extends XmlParser implements ApiResponseReader 24 | { 25 | private static final String 26 | TAG = "tag", 27 | CHANGESET = "changeset", 28 | COMMENT = "comment", 29 | TEXT = "text"; 30 | 31 | private Map users; 32 | 33 | private final Handler handler; 34 | 35 | private ChangesetInfo currentChangesetInfo; 36 | private ChangesetNote currentComment; 37 | private List comments; 38 | private Map tags; 39 | 40 | public ChangesetParser(Handler handler) 41 | { 42 | this.handler = handler; 43 | } 44 | 45 | @Override 46 | public Void parse(InputStream in) throws IOException 47 | { 48 | users = new HashMap<>(); 49 | doParse(in); 50 | users = null; 51 | return null; 52 | } 53 | 54 | @Override 55 | protected void onStartElement() throws ParseException 56 | { 57 | String name = getName(); 58 | 59 | if(CHANGESET.equals(name)) 60 | { 61 | currentChangesetInfo = parseChangeset(); 62 | } 63 | else if(TAG.equals(name)) 64 | { 65 | if(tags == null) 66 | { 67 | tags = new HashMap<>(); 68 | } 69 | tags.put(getAttribute("k"), getAttribute("v")); 70 | } 71 | else if(COMMENT.equals(name)) 72 | { 73 | currentComment = parseChangesetComment(); 74 | } 75 | } 76 | 77 | private ChangesetInfo parseChangeset() throws ParseException 78 | { 79 | BoundingBox bounds = null; 80 | if(getAttribute("min_lat") != null) 81 | { 82 | bounds = new BoundingBox(OsmLatLon.parseLatLon(getAttribute("min_lat"), 83 | getAttribute("min_lon")), OsmLatLon.parseLatLon(getAttribute("max_lat"), 84 | getAttribute("max_lon"))); 85 | } 86 | 87 | String closedAtStr = getAttribute("closed_at"); 88 | Instant closedAt = null; 89 | if(closedAtStr != null) 90 | { 91 | closedAt = Instant.parse(closedAtStr); 92 | } 93 | 94 | User user = parseUser(); 95 | // user must be defined for a changeset 96 | if(user == null) 97 | throw new NullPointerException(); 98 | 99 | ChangesetInfo result = new ChangesetInfo(); 100 | result.id = getLongAttribute("id"); 101 | result.createdAt = Instant.parse(getAttribute("created_at")); 102 | result.closedAt = closedAt; 103 | result.user = user; 104 | result.boundingBox = bounds; 105 | result.isOpen = getBooleanAttribute("open"); 106 | result.notesCount = getIntAttribute("comments_count"); 107 | result.changesCount = getIntAttribute("changes_count"); 108 | return result; 109 | } 110 | 111 | private ChangesetNote parseChangesetComment() throws ParseException 112 | { 113 | ChangesetNote comment = new ChangesetNote(); 114 | comment.id = getLongAttribute("id"); 115 | comment.user = parseUser(); 116 | comment.createdAt = Instant.parse(getAttribute("date")); 117 | return comment; 118 | } 119 | 120 | private User parseUser() 121 | { 122 | Long userId = getLongAttribute("uid"); 123 | if(userId == null) 124 | return null; 125 | 126 | if(!users.containsKey(userId)) 127 | { 128 | User user = new User(userId, getAttribute("user")); 129 | users.put(userId, user); 130 | return user; 131 | } 132 | return users.get(userId); 133 | } 134 | 135 | @Override 136 | protected void onEndElement() 137 | { 138 | String name = getName(); 139 | 140 | if(TEXT.equals(name)) 141 | { 142 | currentComment.text = getText(); 143 | } 144 | if(COMMENT.equals(name)) 145 | { 146 | if(comments == null) 147 | { 148 | comments = new ArrayList<>(); 149 | } 150 | comments.add(currentComment); 151 | currentComment = null; 152 | } 153 | else if(CHANGESET.equals(name)) 154 | { 155 | currentChangesetInfo.tags = tags; 156 | currentChangesetInfo.discussion = comments; 157 | 158 | handler.handle(currentChangesetInfo); 159 | currentChangesetInfo = null; 160 | 161 | tags = null; 162 | comments = null; 163 | } 164 | 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /libs/user/src/main/java/de/westnordost/osmapi/user/UserInfoParser.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.user; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.text.ParseException; 6 | import java.time.Instant; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import de.westnordost.osmapi.ApiResponseReader; 11 | import de.westnordost.osmapi.common.Handler; 12 | import de.westnordost.osmapi.common.XmlParser; 13 | 14 | /** Parses information for users (API 0.6, since 2012). 15 | * 16 | * See https://github.com/openstreetmap/openstreetmap-website/blob/master/app/views/user/api_read.builder 17 | * for what the user actually sends. */ 18 | public class UserInfoParser extends XmlParser implements ApiResponseReader 19 | { 20 | private static final String USER = "user", 21 | ROLES = "roles", 22 | BLOCKS = "blocks", 23 | SOCIAL_LINKS = "social-links"; 24 | 25 | private List roles; 26 | private UserSocialLink link; 27 | 28 | protected Handler handler; 29 | protected UserInfo user; 30 | 31 | public UserInfoParser(Handler handler) 32 | { 33 | this.handler = handler; 34 | } 35 | 36 | @Override 37 | public Void parse(InputStream in) throws IOException 38 | { 39 | doParse(in); 40 | return null; 41 | } 42 | 43 | protected void createUser(long id, String name) 44 | { 45 | user = new UserInfo(id, name); 46 | } 47 | 48 | @Override 49 | protected void onStartElement() throws ParseException 50 | { 51 | String name = getName(); 52 | String parent = getParentName(); 53 | 54 | if(USER.equals(name)) 55 | { 56 | createUser(getLongAttribute("id"),getAttribute("display_name")); 57 | user.createdAt = Instant.parse(getAttribute("account_created")); 58 | } 59 | 60 | if(USER.equals(parent)) 61 | { 62 | switch(name) 63 | { 64 | case "img": 65 | user.profileImageUrl = getAttribute("href"); 66 | break; 67 | case "changesets": 68 | user.changesetsCount = getIntAttribute("count"); 69 | break; 70 | case "traces": 71 | user.gpsTracesCount = getIntAttribute("count"); 72 | break; 73 | case "contributor-terms": 74 | user.hasAgreedToContributorTerms = getBooleanAttribute("agreed"); 75 | break; 76 | } 77 | } 78 | else if(BLOCKS.equals(parent)) 79 | { 80 | if("received".equals(name)) 81 | { 82 | user.isBlocked = getIntAttribute("active") != 0; 83 | /* There is more information that could be parsed here. 84 | But I really do not see any sense for the user of the API to know whether a user 85 | was once blocked but not anymore or the number of blocks that are active. Tell me 86 | if you think otherwise. */ 87 | } 88 | } 89 | else if (SOCIAL_LINKS.equals(parent)) 90 | { 91 | if ("links".equals(name)) 92 | { 93 | link = UserSocialLink(); 94 | link.platform = getAttribute("platform"); 95 | } 96 | } 97 | } 98 | 99 | @Override 100 | protected void onEndElement() 101 | { 102 | String name = getName(); 103 | String parent = getParentName(); 104 | 105 | if(USER.equals(name)) 106 | { 107 | handler.handle( user ); 108 | user = null; 109 | } 110 | else if(ROLES.equals(name)) 111 | { 112 | user.roles = roles; 113 | roles = null; 114 | } 115 | 116 | if(USER.equals(parent)) 117 | { 118 | if("description".equals(name)) 119 | { 120 | user.profileDescription = getText(); 121 | } 122 | else if ("company").equals(name)) 123 | { 124 | user.company = getText(); 125 | } 126 | } 127 | else if(ROLES.equals(parent)) 128 | { 129 | if("role".equals(name)) 130 | { 131 | // the vast majority of users has no roles (but still there is an empty 132 | // element in the xml), so we create the list lazily 133 | if(roles == null) 134 | { 135 | roles = new ArrayList<>(1); 136 | } 137 | roles.add(getText()); 138 | } 139 | } 140 | else if (SOCIAL_LINKS.equals(parent)) 141 | { 142 | if ("link".equals(name)) 143 | { 144 | link.url = getText(); 145 | if (user.socialLinks == null) 146 | { 147 | user.socialLinks = ArrayList<>(); 148 | } 149 | user.socialLinks.add(link); 150 | link = null 151 | } 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /libs/notes/src/main/java/de/westnordost/osmapi/notes/QueryNotesFilters.java: -------------------------------------------------------------------------------- 1 | package de.westnordost.osmapi.notes; 2 | 3 | import de.westnordost.osmapi.map.data.BoundingBox; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.net.URLEncoder; 7 | import java.time.Instant; 8 | import java.time.format.DateTimeFormatter; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class QueryNotesFilters 13 | { 14 | private static final String CHARSET = "UTF-8"; 15 | 16 | private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT; 17 | 18 | private final Map params = new HashMap<>(); 19 | 20 | /** 21 | * @param term search for a certain string 22 | */ 23 | public QueryNotesFilters byTerm(String term) 24 | { 25 | params.put("q", term); 26 | return this; 27 | } 28 | 29 | /** 30 | * @param bounds limit search to the given area 31 | */ 32 | public QueryNotesFilters byBoundingBox(BoundingBox bounds) 33 | { 34 | params.put("bbox", bounds.getAsLeftBottomRightTopString()); 35 | return this; 36 | } 37 | 38 | /** 39 | * @param displayName limit search to only the given user name 40 | * @throws IllegalArgumentException if a user has already been specified by id 41 | */ 42 | public QueryNotesFilters byUser(String displayName) 43 | { 44 | if(params.containsKey("user")) 45 | { 46 | throw new IllegalArgumentException("already provided a user ID"); 47 | } 48 | try 49 | { 50 | params.put("display_name", URLEncoder.encode(displayName, CHARSET)); 51 | } 52 | catch (UnsupportedEncodingException ignore) { } 53 | return this; 54 | } 55 | 56 | /** 57 | * @param userId limit search to only the given user id 58 | * @throws IllegalArgumentException if a user has already been specified by user name 59 | */ 60 | public QueryNotesFilters byUser(long userId) 61 | { 62 | if(params.containsKey("display_name")) 63 | { 64 | throw new IllegalArgumentException("already provided a user name"); 65 | } 66 | params.put("user", String.valueOf(userId)); 67 | return this; 68 | } 69 | 70 | /** 71 | * @param date include notes only created after the given date 72 | */ 73 | public QueryNotesFilters createdAfter(Instant date) 74 | { 75 | params.put("from", FORMATTER.format(date)); 76 | return this; 77 | } 78 | 79 | /** 80 | * @param date include notes only created after the given date. If not specified, now 81 | */ 82 | public QueryNotesFilters createdBefore(Instant date) 83 | { 84 | params.put("to", FORMATTER.format(date)); 85 | return this; 86 | } 87 | 88 | /** 89 | * @param days include closed notes in the search closed not more than x days ago. Default is 90 | * 7 days. A value of -1 means all notes are returned 91 | */ 92 | public QueryNotesFilters hideClosedNotesAfter(int days) 93 | { 94 | params.put("closed", String.valueOf(days)); 95 | return this; 96 | } 97 | 98 | /** 99 | * @param count return at most this many notes. The default can be looked up with the capabilities api call. 100 | * In 2023-11, the default was 100 and the maximum query limit was 10000. 101 | */ 102 | public QueryNotesFilters limit(int count) 103 | { 104 | if(count <= 0) 105 | { 106 | throw new IllegalArgumentException("limit must be positive"); 107 | } 108 | params.put("limit", String.valueOf(count)); 109 | return this; 110 | } 111 | 112 | 113 | /** 114 | * @param property the note property to order the results 115 | * @param order whether to order the results in ascending or descending order 116 | */ 117 | public QueryNotesFilters orderBy(NoteProperty property, Order order) 118 | { 119 | params.put("sort", property.osm); 120 | params.put("order", order.osm); 121 | return this; 122 | } 123 | 124 | public String toParamString() 125 | { 126 | StringBuilder result = new StringBuilder(); 127 | boolean first = true; 128 | for(Map.Entry entry : params.entrySet()) 129 | { 130 | if(first) first = false; 131 | else result.append("&"); 132 | result.append(entry.getKey()); 133 | result.append("="); 134 | 135 | result.append(entry.getValue()); 136 | } 137 | return result.toString(); 138 | } 139 | 140 | /** Specify by which property of notes to order the result */ 141 | public enum NoteProperty 142 | { 143 | /** order by date of creation */ 144 | CREATION_DATE("created_at"), 145 | /** order by update date */ 146 | UPDATE_DATE("updated_at"); 147 | 148 | private final String osm; 149 | 150 | private NoteProperty(String osm) 151 | { 152 | this.osm = osm; 153 | } 154 | } 155 | 156 | public enum Order { 157 | /** newest notes first */ 158 | DESCENDING("newest"), 159 | /** oldest notes first */ 160 | ASCENDING("oldest"); 161 | 162 | private final String osm; 163 | 164 | private Order(String osm) 165 | { 166 | this.osm = osm; 167 | } 168 | } 169 | } 170 | --------------------------------------------------------------------------------