├── data ├── ar.lang ├── ast.lang ├── be.lang ├── bg.lang ├── ca.lang ├── cs.lang ├── cy.lang ├── da.lang ├── de.lang ├── el.lang ├── en.lang ├── eo.lang ├── es.lang ├── et.lang ├── fa.lang ├── fi.lang ├── fo.lang ├── fr.lang ├── gl.lang ├── hu.lang ├── id.lang ├── is.lang ├── it.lang ├── ja.lang ├── km.lang ├── ko.lang ├── lt.lang ├── mr.lang ├── nb.lang ├── nl.lang ├── pl.lang ├── pt.lang ├── ru.lang ├── sk.lang ├── sv.lang ├── tr.lang ├── uk.lang ├── en_AU.lang ├── en_CA.lang ├── en_GB.lang ├── pt_BR.lang ├── zh_CN.lang ├── zh_TW.lang ├── sr-latin.lang └── ca-valencia.lang ├── images ├── changeset-viewer.png ├── changeset_layer.png ├── dialogs │ └── changeset.png └── mapmode │ └── getchangeset.png ├── .gitignore ├── .classpath ├── .project ├── src └── org │ └── openstreetmap │ └── josm │ └── plugins │ └── changeset │ ├── ChangesetViewerPlugin.java │ ├── Changeset.java │ ├── ChangesetDraw.java │ ├── util │ ├── ChangesetBeen.java │ ├── Config.java │ ├── CellRenderer.java │ ├── Request.java │ ├── ChangesetController.java │ └── DataSetChangesetBuilder.java │ ├── ChangesetLayer.java │ └── ChangesetDialog.java ├── README.md ├── LICENSE ├── pom.xml ├── .github └── workflows │ └── ant.yml └── .settings └── org.eclipse.jdt.core.prefs /data/ar.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/ar.lang -------------------------------------------------------------------------------- /data/ast.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/ast.lang -------------------------------------------------------------------------------- /data/be.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/be.lang -------------------------------------------------------------------------------- /data/bg.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/bg.lang -------------------------------------------------------------------------------- /data/ca.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/ca.lang -------------------------------------------------------------------------------- /data/cs.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/cs.lang -------------------------------------------------------------------------------- /data/cy.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/cy.lang -------------------------------------------------------------------------------- /data/da.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/da.lang -------------------------------------------------------------------------------- /data/de.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/de.lang -------------------------------------------------------------------------------- /data/el.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/el.lang -------------------------------------------------------------------------------- /data/en.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/en.lang -------------------------------------------------------------------------------- /data/eo.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/eo.lang -------------------------------------------------------------------------------- /data/es.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/es.lang -------------------------------------------------------------------------------- /data/et.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/et.lang -------------------------------------------------------------------------------- /data/fa.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/fa.lang -------------------------------------------------------------------------------- /data/fi.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/fi.lang -------------------------------------------------------------------------------- /data/fo.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/fo.lang -------------------------------------------------------------------------------- /data/fr.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/fr.lang -------------------------------------------------------------------------------- /data/gl.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/gl.lang -------------------------------------------------------------------------------- /data/hu.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/hu.lang -------------------------------------------------------------------------------- /data/id.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/id.lang -------------------------------------------------------------------------------- /data/is.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/is.lang -------------------------------------------------------------------------------- /data/it.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/it.lang -------------------------------------------------------------------------------- /data/ja.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/ja.lang -------------------------------------------------------------------------------- /data/km.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/km.lang -------------------------------------------------------------------------------- /data/ko.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/ko.lang -------------------------------------------------------------------------------- /data/lt.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/lt.lang -------------------------------------------------------------------------------- /data/mr.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/mr.lang -------------------------------------------------------------------------------- /data/nb.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/nb.lang -------------------------------------------------------------------------------- /data/nl.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/nl.lang -------------------------------------------------------------------------------- /data/pl.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/pl.lang -------------------------------------------------------------------------------- /data/pt.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/pt.lang -------------------------------------------------------------------------------- /data/ru.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/ru.lang -------------------------------------------------------------------------------- /data/sk.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/sk.lang -------------------------------------------------------------------------------- /data/sv.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/sv.lang -------------------------------------------------------------------------------- /data/tr.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/tr.lang -------------------------------------------------------------------------------- /data/uk.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/uk.lang -------------------------------------------------------------------------------- /data/en_AU.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/en_AU.lang -------------------------------------------------------------------------------- /data/en_CA.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/en_CA.lang -------------------------------------------------------------------------------- /data/en_GB.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/en_GB.lang -------------------------------------------------------------------------------- /data/pt_BR.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/pt_BR.lang -------------------------------------------------------------------------------- /data/zh_CN.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/zh_CN.lang -------------------------------------------------------------------------------- /data/zh_TW.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/zh_TW.lang -------------------------------------------------------------------------------- /data/sr-latin.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/sr-latin.lang -------------------------------------------------------------------------------- /data/ca-valencia.lang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/data/ca-valencia.lang -------------------------------------------------------------------------------- /images/changeset-viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/images/changeset-viewer.png -------------------------------------------------------------------------------- /images/changeset_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/images/changeset_layer.png -------------------------------------------------------------------------------- /images/dialogs/changeset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/images/dialogs/changeset.png -------------------------------------------------------------------------------- /images/mapmode/getchangeset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JOSM/changeset-viewer/HEAD/images/mapmode/getchangeset.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Some Files # 2 | ################### 3 | nbproject/ 4 | build/ 5 | *.class 6 | *.log 7 | /bin/ 8 | /javadoc/ 9 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | JOSM-changeset-viewer 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.sonarlint.eclipse.core.sonarlintBuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/ChangesetViewerPlugin.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset; 3 | 4 | import java.awt.GraphicsEnvironment; 5 | import org.openstreetmap.josm.gui.MapFrame; 6 | import org.openstreetmap.josm.plugins.Plugin; 7 | import org.openstreetmap.josm.plugins.PluginInformation; 8 | 9 | /** 10 | * The base plugin class 11 | * @author ruben 12 | */ 13 | public class ChangesetViewerPlugin extends Plugin { 14 | 15 | public ChangesetViewerPlugin(PluginInformation info) { 16 | super(info); 17 | } 18 | 19 | @Override 20 | public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { 21 | if (newFrame != null && !GraphicsEnvironment.isHeadless()) { 22 | newFrame.addToggleDialog(new ChangesetDialog()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/Changeset.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset; 3 | 4 | import static org.openstreetmap.josm.tools.I18n.tr; 5 | 6 | import org.openstreetmap.josm.plugins.changeset.util.DataSetChangesetBuilder.BoundedChangesetDataSet; 7 | 8 | /** 9 | * A class to draw a changeset layer 10 | * @author ruben 11 | */ 12 | public final class Changeset { 13 | private Changeset() { 14 | // Hide constructor 15 | } 16 | 17 | /** 18 | * Do the work to show the data 19 | * @param data The data to show 20 | * @param changesetId The changeset id to show 21 | */ 22 | public static void work(BoundedChangesetDataSet data, String changesetId) { 23 | ChangesetLayer tofixLayer = new ChangesetLayer(tr("Changeset: {0}", changesetId)); 24 | ChangesetDraw.draw(tofixLayer, data); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Changeset Viewer Plugin 2 | 3 | Changeset Viewer plugin allows you to visualize one or many changesets in JOSM! 4 | 5 | ![changeset-viewer](https://user-images.githubusercontent.com/1152236/35937653-deae6742-0c14-11e8-84a0-d65039afac45.gif) 6 | 7 | 8 | ## How it works? 9 | 10 | Set the map view in the desired location, click the `Get Changeset in the area` button and the plugin will download changesets that affected this area. It can display more than one changeset at a time. 11 | 12 | If you want to display a particular changeset, you can specify the changeset id in the text field and press the button `Display changeset`. 13 | 14 | Look at the screenshot below 👇 15 | 16 | ![Legend](https://user-images.githubusercontent.com/1152236/35940490-5e217c64-0c1d-11e8-96d8-3098d6414a9a.png) 17 | 18 | 19 | This plugin utilizes [OSMCha](https://osmcha.org/) and [Changeset-Map](https://github.com/osmlab/changeset-map) APIs capabilities. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018, Development Seed 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/ChangesetDraw.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset; 3 | 4 | import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 5 | import org.openstreetmap.josm.gui.MainApplication; 6 | import org.openstreetmap.josm.plugins.changeset.util.DataSetChangesetBuilder.BoundedChangesetDataSet; 7 | 8 | /** 9 | * Draw changesets 10 | * @author ruben 11 | */ 12 | public final class ChangesetDraw { 13 | private ChangesetDraw() { 14 | // Hide constructor 15 | } 16 | 17 | /** 18 | * Draw the changeset 19 | * @param tofixNewLayer The layer to draw 20 | * @param data The data to draw on the layer 21 | */ 22 | public static void draw(final ChangesetLayer tofixNewLayer, BoundedChangesetDataSet data) { 23 | if (data == null) { 24 | return; 25 | } 26 | BoundingXYVisitor v = new BoundingXYVisitor(); 27 | v.visit(data.getBounds()); 28 | MainApplication.getMap().mapView.zoomTo(v); 29 | if (!MainApplication.getLayerManager().containsLayer(tofixNewLayer)) { 30 | MainApplication.getLayerManager().addLayer(tofixNewLayer); 31 | } 32 | tofixNewLayer.setDataSet(data); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/util/ChangesetBeen.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset.util; 3 | 4 | /** 5 | * A record class for changeset metadata 6 | * @author ruben 7 | */ 8 | public class ChangesetBeen { 9 | 10 | String date; 11 | String user; 12 | int changesetId; 13 | int delete; 14 | int create; 15 | int modify; 16 | 17 | public String getUser() { 18 | return user; 19 | } 20 | 21 | public void setUser(String user) { 22 | this.user = user; 23 | } 24 | 25 | public int getChangesetId() { 26 | return changesetId; 27 | } 28 | 29 | public void setChangesetId(int changesetId) { 30 | this.changesetId = changesetId; 31 | } 32 | 33 | public int getDelete() { 34 | return delete; 35 | } 36 | 37 | public void setDelete(int delete) { 38 | this.delete = delete; 39 | } 40 | 41 | public int getCreate() { 42 | return create; 43 | } 44 | 45 | public void setCreate(int create) { 46 | this.create = create; 47 | } 48 | 49 | public int getModify() { 50 | return modify; 51 | } 52 | 53 | public void setModify(int modify) { 54 | this.modify = modify; 55 | } 56 | 57 | public String getDate() { 58 | return date; 59 | } 60 | 61 | public void setDate(String date) { 62 | this.date = date; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/util/Config.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset.util; 3 | 4 | /** 5 | * A class storing config values 6 | * @author ruben 7 | */ 8 | public final class Config { 9 | private Config() { 10 | // Hide constructor 11 | } 12 | 13 | public static final String OSMCHA_HOST = "https://osmcha.org/"; 14 | public static final String OSMCHA_HOST_API = OSMCHA_HOST + "api/v1/"; 15 | public static final String OSMCHA_HOST_CHANGESETS = OSMCHA_HOST_API + "changesets/?"; 16 | public static final String HOST = "https://s3.amazonaws.com/mapbox/real-changesets/production/"; 17 | public static final String CHANGESET_MAP = "https://osmlab.github.io/changeset-map/"; 18 | public static final String OSMCHANGESET = "https://www.openstreetmap.org/changeset/"; 19 | private static int page = 1; 20 | private static String pageSize = "page_size=75"; 21 | private static String bbox = "none"; 22 | private static final String areaLt = "area_lt=1"; 23 | 24 | public static int getPAGE() { 25 | return page; 26 | } 27 | 28 | public static void setPAGE(int page) { 29 | Config.page = page; 30 | } 31 | 32 | public static String getPAGE_SIZE() { 33 | return pageSize; 34 | } 35 | 36 | public static void setPAGE_SIZE(String pageSize) { 37 | Config.pageSize = pageSize; 38 | } 39 | 40 | public static String getBBOX() { 41 | return bbox; 42 | } 43 | 44 | public static void setBBOX(String bbox) { 45 | Config.bbox = "in_bbox=" + bbox; 46 | } 47 | 48 | public static String getHost() { 49 | if ("none".equals(bbox)) { 50 | return OSMCHA_HOST_CHANGESETS + "page=" + page + "&" + pageSize + "&" + areaLt; 51 | } else { 52 | return OSMCHA_HOST_CHANGESETS + "page=" + page + "&" + pageSize + "&" + bbox + "&" + areaLt; 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.openstreetmap.josm.plugins 6 | plugin-root 7 | 1.0-SNAPSHOT 8 | 9 | changeset-viewer 10 | 11 | ${plugin.link} 12 | 13 | 14 | Rub21 15 | 16 | 17 | 18 | src 19 | 19044 20 | Rub21 21 | org.openstreetmap.josm.plugins.changeset.ChangesetViewerPlugin 22 | Changeset Viewer plugin allows you to visualize the changesets in JOSM background 23 | images/changeset-viewer.png 24 | https://github.com/JOSM/changeset-viewer 25 | true 26 | 27 | 28 | 29 | 30 | org.apache.maven.plugins 31 | maven-jar-plugin 32 | 33 | 34 | 35 | ${plugin.link} 36 | ${plugin.icon} 37 | ${plugin.canloadatruntime} 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /.github/workflows/ant.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - $default-branch 8 | - $protected-branches 9 | pull_request: 10 | branches: 11 | - master 12 | - $default-branch 13 | workflow_dispatch: 14 | 15 | permissions: 16 | id-token: write 17 | attestations: write 18 | contents: write 19 | packages: write 20 | checks: write 21 | 22 | jobs: 23 | check-release-needed: 24 | runs-on: ubuntu-latest 25 | outputs: 26 | release_needed: ${{ steps.create_release_needed.outputs.release_needed }} 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | with: 31 | fetch-depth: ${{ (github.repository == 'JOSM/changeset-viewer' && github.ref_type == 'branch' && github.ref_name == 'master' && github.event_name != 'schedule' && github.event_name != 'pull_request') && '0' || '1' }} 32 | 33 | - name: Set release needed 34 | id: create_release_needed 35 | run: | 36 | last_tag=$(git describe --tags --abbrev=0 --always) 37 | release_needed="false" 38 | for file in $(git diff ${last_tag}..HEAD --name-only); do 39 | if [[ $file = "src/"* ]] || [[ $file = "data/*" ]] || [[ $file = "lib/*" ]] || [[ $file = "resources/*" ]] || [[ $file = "images/*" ]]; then 40 | release_needed="true" 41 | break 42 | fi 43 | done 44 | echo "release_needed=$release_needed" >> $GITHUB_OUTPUT 45 | 46 | call-workflow: 47 | needs: check-release-needed 48 | strategy: 49 | matrix: 50 | josm-revision: ["", "r19044"] 51 | uses: JOSM/JOSMPluginAction/.github/workflows/ant.yml@v3 52 | with: 53 | josm-revision: ${{ matrix.josm-revision }} 54 | perform-revision-tagging: ${{ github.repository == 'JOSM/changeset-viewer' && github.ref_type == 'branch' && github.ref_name == 'master' && github.event_name != 'schedule' && github.event_name != 'pull_request' && matrix.josm-revision == 'r19044' }} 55 | secrets: inherit 56 | permissions: 57 | attestations: write 58 | checks: write 59 | contents: write 60 | deployments: write 61 | id-token: write 62 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/util/CellRenderer.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset.util; 3 | 4 | import java.awt.Color; 5 | import java.awt.Component; 6 | import java.awt.Font; 7 | import java.awt.GridLayout; 8 | import javax.swing.BorderFactory; 9 | import javax.swing.JLabel; 10 | import javax.swing.JList; 11 | import javax.swing.JPanel; 12 | import javax.swing.ListCellRenderer; 13 | import javax.swing.SwingConstants; 14 | 15 | /** 16 | * The cell renderer for {@link ChangesetBeen} 17 | * @author ruben 18 | */ 19 | public class CellRenderer implements ListCellRenderer { 20 | 21 | @Override 22 | public Component getListCellRendererComponent(JList list, ChangesetBeen changesetBeen, int index, 23 | boolean isSelected, boolean cellHasFocus) { 24 | JPanel jPanelChangeset = new JPanel(new GridLayout(2, 1, 8, 8)); 25 | if (changesetBeen != null) { 26 | JPanel jPanelRow = new JPanel(new GridLayout(1, 3, 10, 10)); 27 | JPanel jPanelNumChanges = new JPanel(new GridLayout(1, 2, 10, 10)); 28 | jPanelNumChanges.setBackground(Color.BLACK); 29 | JLabel jLabelDate = new JLabel(changesetBeen.getDate(), SwingConstants.CENTER); 30 | jLabelDate.setForeground(new Color(0, 174, 255)); 31 | jPanelChangeset.setBorder(BorderFactory.createTitledBorder("Changeset :" + changesetBeen.getChangesetId())); 32 | JLabel jLabelUser = new JLabel(changesetBeen.getUser(), SwingConstants.CENTER); 33 | JLabel jLabelCreate = new JLabel(String.valueOf(changesetBeen.getCreate()), SwingConstants.CENTER); 34 | jLabelCreate.setForeground(new Color(50, 214, 184)); 35 | jLabelCreate.setFont(new Font("Serif", Font.BOLD, 12)); 36 | JLabel jLabelModify = new JLabel(String.valueOf(changesetBeen.getModify()), SwingConstants.CENTER); 37 | jLabelModify.setForeground(new Color(229, 228, 61)); 38 | jLabelModify.setFont(new Font("Serif", Font.BOLD, 12)); 39 | JLabel jLabelDelete = new JLabel(String.valueOf(changesetBeen.getDelete()), SwingConstants.CENTER); 40 | jLabelDelete.setForeground(new Color(197, 38, 63)); 41 | jLabelDelete.setFont(new Font("Serif", Font.BOLD, 12)); 42 | jPanelRow.add(jLabelUser); 43 | jPanelNumChanges.add(jLabelCreate); 44 | jPanelNumChanges.add(jLabelModify); 45 | jPanelNumChanges.add(jLabelDelete); 46 | jPanelRow.add(jPanelNumChanges); 47 | //last 48 | jPanelChangeset.add(jLabelDate); 49 | jPanelChangeset.add(jPanelRow); 50 | } 51 | return jPanelChangeset; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/util/Request.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset.util; 3 | 4 | import static org.openstreetmap.josm.tools.I18n.tr; 5 | 6 | import java.io.IOException; 7 | import java.net.URL; 8 | 9 | import javax.swing.BoxLayout; 10 | import javax.swing.JPanel; 11 | 12 | import org.openstreetmap.josm.data.preferences.StringProperty; 13 | import org.openstreetmap.josm.gui.ExtendedDialog; 14 | import org.openstreetmap.josm.gui.MainApplication; 15 | import org.openstreetmap.josm.gui.widgets.HtmlPanel; 16 | import org.openstreetmap.josm.gui.widgets.JosmTextField; 17 | import org.openstreetmap.josm.tools.HttpClient; 18 | import org.openstreetmap.josm.tools.HttpClient.Response; 19 | import org.openstreetmap.josm.tools.Logging; 20 | 21 | /** 22 | * A helper class to fetch content 23 | * @author ruben 24 | */ 25 | public final class Request { 26 | private static final StringProperty OSMCHA_AUTHORIZATION = new StringProperty("osmcha.authorization.key", null); 27 | private Request() { 28 | // Hide constructor 29 | } 30 | 31 | /** 32 | * Get the OSMCha authorization key 33 | * @return {@code true} if the user entered an authorization key 34 | */ 35 | private static boolean getOsmChaAuthorization() { 36 | JPanel jPanel = new JPanel(); 37 | jPanel.setLayout(new BoxLayout(jPanel, BoxLayout.Y_AXIS)); 38 | String message = tr("Please enter your OSMCha API Key (see link)"); 39 | String userUrl = Config.OSMCHA_HOST + "user"; 40 | HtmlPanel htmlPanel = new HtmlPanel("" + message 41 | + "
" + userUrl + ""); 42 | htmlPanel.enableClickableHyperlinks(); 43 | jPanel.add(htmlPanel); 44 | JosmTextField textField = new JosmTextField(); 45 | textField.setHint("Token "); 46 | jPanel.add(textField); 47 | ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), 48 | tr("Missing OSMCha API Key"), tr("OK"), tr("Cancel")) 49 | .setContent(jPanel); 50 | int ret = ed.showDialog().getValue(); 51 | if (ret == ExtendedDialog.DialogClosedOtherwise || ret == 2) { 52 | return false; 53 | } 54 | OSMCHA_AUTHORIZATION.put(textField.getText()); 55 | return OSMCHA_AUTHORIZATION.isSet(); 56 | } 57 | 58 | /** 59 | * Get a URL 60 | * @param url The url to GET 61 | * @return The result (or null) 62 | * @throws IOException if we couldn't connect 63 | */ 64 | public static String sendGET(String url) throws IOException { 65 | Logging.trace(url); 66 | HttpClient client = HttpClient.create(new URL(url)) 67 | .setAccept("application/json"); 68 | if (url.contains(Config.OSMCHA_HOST_API)) { 69 | if (!OSMCHA_AUTHORIZATION.isSet() && !getOsmChaAuthorization()) { 70 | // They haven't authorized osmcha before, and they declined authorization now 71 | return null; 72 | } 73 | client.setHeader("Authorization", OSMCHA_AUTHORIZATION.get()); 74 | } 75 | Response response = client.connect(); 76 | String result = null; 77 | if (response.getResponseCode() == 200) { 78 | result = response.fetchContent(); 79 | } else if (response.getResponseCode() == 401 && url.contains(Config.OSMCHA_HOST_API)) { 80 | OSMCHA_AUTHORIZATION.remove(); 81 | } 82 | response.disconnect(); 83 | return result; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/util/ChangesetController.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset.util; 3 | 4 | import java.io.IOException; 5 | import java.io.StringReader; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | import jakarta.json.Json; 9 | import jakarta.json.JsonArray; 10 | import jakarta.json.JsonObject; 11 | import jakarta.json.JsonReader; 12 | import jakarta.json.JsonValue; 13 | import jakarta.json.stream.JsonParsingException; 14 | import org.openstreetmap.josm.plugins.changeset.util.DataSetChangesetBuilder.BoundedChangesetDataSet; 15 | import org.openstreetmap.josm.tools.Logging; 16 | 17 | /** 18 | * Get changesets to show the user and show specific changesets 19 | * @author ruben 20 | */ 21 | public final class ChangesetController { 22 | private ChangesetController() { 23 | // Hide the constructor 24 | } 25 | 26 | /** 27 | * Get a changeset 28 | * @param changesetId The changeset to get 29 | * @return The dataset to show 30 | */ 31 | public static BoundedChangesetDataSet getChangeset(String changesetId) { 32 | DataSetChangesetBuilder builder = new DataSetChangesetBuilder(); 33 | try { 34 | String url = Config.HOST + changesetId + ".json"; 35 | String stringChangeset = Request.sendGET(url); 36 | if (stringChangeset == null) { 37 | return null; 38 | } else { 39 | return builder.build(stringChangeset); 40 | } 41 | } catch (IOException ex) { 42 | Logger.getLogger(ChangesetController.class.getName()).log(Level.SEVERE, null, ex); 43 | return null; 44 | } 45 | } 46 | 47 | /** 48 | * Get a list of changesets 49 | * @return The changeset array 50 | */ 51 | public static ChangesetBeen[] getListChangeset() { 52 | ChangesetBeen[] changesets = new ChangesetBeen[75]; 53 | try { 54 | String stringChangesets = Request.sendGET(Config.getHost()); 55 | if (stringChangesets == null) { 56 | return changesets; 57 | } else { 58 | try (JsonReader jsonReader = Json.createReader(new StringReader(stringChangesets))) { 59 | JsonObject jsonObject = jsonReader.readObject(); 60 | JsonArray jsonArray = jsonObject.getJsonArray("features"); 61 | int i = 0; 62 | for (JsonValue value : jsonArray) { 63 | try (JsonReader jsonReader2 = Json.createReader(new StringReader(value.toString()))) { 64 | ChangesetBeen changesetBeen = new ChangesetBeen(); 65 | JsonObject jsonChangeset = jsonReader2.readObject(); 66 | changesetBeen.setChangesetId(jsonChangeset.getInt("id")); 67 | JsonObject properties = jsonChangeset.getJsonObject("properties"); 68 | changesetBeen.setUser(properties.getString("user")); 69 | changesetBeen.setDelete(properties.getInt("delete")); 70 | changesetBeen.setCreate(properties.getInt("create")); 71 | changesetBeen.setModify(properties.getInt("modify")); 72 | changesetBeen.setDate(properties.getString("date")); 73 | changesets[i] = changesetBeen; 74 | i++; 75 | } 76 | } 77 | return changesets; 78 | } catch (JsonParsingException ex) { 79 | Logging.error(ex); 80 | return changesets; 81 | } 82 | } 83 | } catch (IOException ex) { 84 | Logging.error(ex); 85 | return changesets; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled 3 | org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore 4 | org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull 5 | org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= 6 | org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault 7 | org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= 8 | org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable 9 | org.eclipse.jdt.core.compiler.annotation.nullable.secondary= 10 | org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled 11 | org.eclipse.jdt.core.compiler.problem.APILeak=warning 12 | org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning 13 | org.eclipse.jdt.core.compiler.problem.autoboxing=ignore 14 | org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning 15 | org.eclipse.jdt.core.compiler.problem.deadCode=warning 16 | org.eclipse.jdt.core.compiler.problem.deprecation=warning 17 | org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled 18 | org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled 19 | org.eclipse.jdt.core.compiler.problem.discouragedReference=warning 20 | org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore 21 | org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore 22 | org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore 23 | org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled 24 | org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore 25 | org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning 26 | org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning 27 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=error 28 | org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning 29 | org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled 30 | org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning 31 | org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning 32 | org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore 33 | org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore 34 | org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning 35 | org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore 36 | org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore 37 | org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled 38 | org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore 39 | org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore 40 | org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled 41 | org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore 42 | org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore 43 | org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning 44 | org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning 45 | org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore 46 | org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning 47 | org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning 48 | org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error 49 | org.eclipse.jdt.core.compiler.problem.nullReference=warning 50 | org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error 51 | org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning 52 | org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning 53 | org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore 54 | org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning 55 | org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore 56 | org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore 57 | org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore 58 | org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning 59 | org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning 60 | org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore 61 | org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore 62 | org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore 63 | org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore 64 | org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore 65 | org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled 66 | org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning 67 | org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled 68 | org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled 69 | org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled 70 | org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore 71 | org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning 72 | org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning 73 | org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled 74 | org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning 75 | org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning 76 | org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore 77 | org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning 78 | org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning 79 | org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled 80 | org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info 81 | org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore 82 | org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore 83 | org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore 84 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore 85 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled 86 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled 87 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled 88 | org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore 89 | org.eclipse.jdt.core.compiler.problem.unusedImport=warning 90 | org.eclipse.jdt.core.compiler.problem.unusedLabel=warning 91 | org.eclipse.jdt.core.compiler.problem.unusedLocal=warning 92 | org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore 93 | org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore 94 | org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled 95 | org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled 96 | org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled 97 | org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning 98 | org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore 99 | org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning 100 | org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning 101 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/ChangesetLayer.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset; 3 | 4 | import static org.openstreetmap.josm.tools.I18n.marktr; 5 | import static org.openstreetmap.josm.tools.I18n.tr; 6 | 7 | import java.awt.BasicStroke; 8 | import java.awt.Color; 9 | import java.awt.Graphics2D; 10 | import java.awt.Point; 11 | import java.awt.Stroke; 12 | import java.awt.event.ActionEvent; 13 | import java.awt.event.ActionListener; 14 | import java.util.List; 15 | 16 | import javax.swing.Action; 17 | import javax.swing.Icon; 18 | import javax.swing.JOptionPane; 19 | 20 | import org.openstreetmap.josm.data.Bounds; 21 | import org.openstreetmap.josm.data.osm.DataSet; 22 | import org.openstreetmap.josm.data.osm.Node; 23 | import org.openstreetmap.josm.data.osm.Way; 24 | import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 25 | import org.openstreetmap.josm.data.preferences.CachingProperty; 26 | import org.openstreetmap.josm.data.preferences.NamedColorProperty; 27 | import org.openstreetmap.josm.gui.MapView; 28 | import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 29 | import org.openstreetmap.josm.gui.dialogs.LayerListPopup; 30 | import org.openstreetmap.josm.gui.layer.Layer; 31 | import org.openstreetmap.josm.plugins.changeset.util.DataSetChangesetBuilder.BoundedChangesetDataSet; 32 | import org.openstreetmap.josm.tools.ImageProvider; 33 | 34 | /** 35 | * A layer to show what a changeset did 36 | * @author ruben 37 | */ 38 | public class ChangesetLayer extends Layer implements ActionListener { 39 | private static final CachingProperty DELETED_COLOR = new NamedColorProperty(NamedColorProperty.COLOR_CATEGORY_GENERAL, 40 | marktr("changeset-viewer"), marktr("Deleted objects"), new Color(197, 38, 63)).cached(); 41 | private static final CachingProperty CREATED_COLOR = new NamedColorProperty(NamedColorProperty.COLOR_CATEGORY_GENERAL, 42 | marktr("changeset-viewer"), marktr("Created objects"), new Color(50, 214, 184)).cached(); 43 | private static final CachingProperty MODIFIED_OLD = new NamedColorProperty(NamedColorProperty.COLOR_CATEGORY_GENERAL, 44 | marktr("changeset-viewer"), marktr("Modified objects (old)"), new Color(214, 138, 13)).cached(); 45 | private static final CachingProperty MODIFIED_NEW = new NamedColorProperty(NamedColorProperty.COLOR_CATEGORY_GENERAL, 46 | marktr("changeset-viewer"), marktr("Modified objects (new)"), new Color(229, 228, 61)).cached(); 47 | 48 | BoundedChangesetDataSet dataSet; 49 | 50 | /** 51 | * Create a new {@link ChangesetLayer} 52 | * @param name The name of the layer 53 | */ 54 | public ChangesetLayer(String name) { 55 | super(name); 56 | } 57 | 58 | @Override 59 | public Icon getIcon() { 60 | return ImageProvider.get("changeset_layer"); 61 | } 62 | 63 | @Override 64 | public String getToolTipText() { 65 | return tr("Layer to draw OSM error"); 66 | } 67 | 68 | @Override 69 | public boolean isMergable(Layer other) { 70 | return false; 71 | } 72 | 73 | /** 74 | * Set the dataset for this layer 75 | * @param dataSet The dataset to show the user 76 | */ 77 | public void setDataSet(BoundedChangesetDataSet dataSet) { 78 | this.dataSet = dataSet; 79 | invalidate(); 80 | } 81 | 82 | @Override 83 | public void paint(Graphics2D g, final MapView mv, Bounds bounds) { 84 | DataSet data = dataSet.getDataSet(); 85 | Stroke stroke = g.getStroke(); 86 | if (data == null) { 87 | return; 88 | } 89 | //Print the objects 90 | final float[] dash1 = {10.0f}; 91 | final BasicStroke defaultStroke = new BasicStroke(2f); 92 | for (Way way : data.searchWays(bounds.toBBox())) { 93 | g.setStroke(defaultStroke); 94 | final String action = way.get("action"); 95 | paintWay(g, mv, dash1, way, action); 96 | } 97 | for (Node node : data.searchNodes(bounds.toBBox())) { 98 | g.setStroke(defaultStroke); 99 | final String action = node.get("action"); 100 | paintNode(g, mv, node, action); 101 | } 102 | g.setStroke(stroke); 103 | } 104 | 105 | private static void paintWay(Graphics2D g, MapView mv, float[] dash1, Way way, String action) { 106 | switch (action) { 107 | case "create": 108 | g.setColor(CREATED_COLOR.get()); 109 | break; 110 | case "delete": 111 | g.setColor(DELETED_COLOR.get()); 112 | break; 113 | case "modify-old": 114 | g.setColor(MODIFIED_OLD.get()); 115 | break; 116 | case "modify-new": 117 | g.setColor(MODIFIED_NEW.get()); 118 | break; 119 | case "modify-new-rel": 120 | case "modify-old-rel": 121 | case "create-rel": 122 | case "delete-rel": 123 | setRelationColor(g, action); 124 | g.setStroke(new BasicStroke(1.0f, 125 | BasicStroke.CAP_BUTT, 126 | BasicStroke.JOIN_ROUND, 127 | 10.0f, dash1, 0.0f)); 128 | break; 129 | default: 130 | throw new IllegalArgumentException("Unknown action: " + action); 131 | } 132 | List nodes = way.getNodes(); 133 | if (nodes.size() < 2) { 134 | return; 135 | } 136 | // We cannot use MapViewPath 137 | Point previous = null; 138 | for (Node node : way.getNodes()) { 139 | final boolean latLonKnown = node.isLatLonKnown(); 140 | if (previous == null && latLonKnown) { 141 | previous = mv.getPoint(node); 142 | continue; 143 | } else if (previous != null && !latLonKnown) { 144 | previous = null; 145 | continue; 146 | } 147 | if (latLonKnown) { 148 | Point point = mv.getPoint(node); 149 | g.drawLine(previous.x, previous.y, point.x, point.y); 150 | previous = point; 151 | } 152 | } 153 | } 154 | 155 | private static void setRelationColor(Graphics2D g, String action) { 156 | switch (action) { 157 | case "modify-new-rel": 158 | g.setColor(MODIFIED_NEW.get()); 159 | break; 160 | case "modify-old-rel": 161 | g.setColor(MODIFIED_OLD.get()); 162 | break; 163 | case "create-rel": 164 | g.setColor(CREATED_COLOR.get()); 165 | break; 166 | case "delete-rel": 167 | g.setColor(DELETED_COLOR.get()); 168 | break; 169 | default: 170 | throw new IllegalArgumentException("Unknown action: " + action); 171 | } 172 | } 173 | 174 | private static void paintNode(Graphics2D g, MapView mv, Node node, String action) { 175 | if (!node.referrers(Way.class).findAny().isPresent()) { 176 | switch (action) { 177 | case "create": 178 | g.setColor(CREATED_COLOR.get()); 179 | break; 180 | case "delete": 181 | g.setColor(DELETED_COLOR.get()); 182 | break; 183 | case "modify-old": 184 | g.setColor(MODIFIED_OLD.get()); 185 | break; 186 | case "modify-new": 187 | g.setColor(MODIFIED_NEW.get()); 188 | break; 189 | default: 190 | throw new IllegalArgumentException("Unknown action: " + action); 191 | } 192 | 193 | Point pnt = mv.getPoint(node); 194 | g.fillOval(pnt.x, pnt.y, 7, 7); 195 | } 196 | } 197 | 198 | @Override 199 | public void visitBoundingBox(BoundingXYVisitor v) { 200 | // nothing to do here 201 | } 202 | 203 | @Override 204 | public Object getInfoComponent() { 205 | return getToolTipText(); 206 | } 207 | 208 | @Override 209 | public Action[] getMenuEntries() { 210 | return new Action[]{ 211 | LayerListDialog.getInstance().createShowHideLayerAction(), 212 | SeparatorLayerAction.INSTANCE, 213 | SeparatorLayerAction.INSTANCE, 214 | new LayerListPopup.InfoAction(this)}; 215 | } 216 | 217 | @Override 218 | public void actionPerformed(ActionEvent e) { 219 | JOptionPane.showConfirmDialog(null, e.getSource()); 220 | } 221 | 222 | @Override 223 | public void mergeFrom(Layer layer) { 224 | throw new UnsupportedOperationException("Layer merge is not supported"); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/ChangesetDialog.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset; 3 | 4 | import static org.openstreetmap.josm.tools.I18n.tr; 5 | 6 | import java.awt.GridBagConstraints; 7 | import java.awt.GridBagLayout; 8 | import java.awt.event.ActionEvent; 9 | import java.awt.event.ActionListener; 10 | import java.awt.event.KeyEvent; 11 | import java.util.Arrays; 12 | import java.util.concurrent.Future; 13 | 14 | import javax.swing.AbstractAction; 15 | import javax.swing.BorderFactory; 16 | import javax.swing.JButton; 17 | import javax.swing.JComboBox; 18 | import javax.swing.JOptionPane; 19 | import javax.swing.JPanel; 20 | import javax.swing.JProgressBar; 21 | import javax.swing.ListCellRenderer; 22 | 23 | import org.openstreetmap.josm.data.Bounds; 24 | import org.openstreetmap.josm.gui.MainApplication; 25 | import org.openstreetmap.josm.gui.MapView; 26 | import org.openstreetmap.josm.gui.SideButton; 27 | import org.openstreetmap.josm.gui.dialogs.ToggleDialog; 28 | import org.openstreetmap.josm.gui.util.GuiHelper; 29 | import org.openstreetmap.josm.gui.widgets.JosmTextField; 30 | import org.openstreetmap.josm.plugins.changeset.util.CellRenderer; 31 | import org.openstreetmap.josm.plugins.changeset.util.ChangesetBeen; 32 | import org.openstreetmap.josm.plugins.changeset.util.ChangesetController; 33 | import org.openstreetmap.josm.plugins.changeset.util.Config; 34 | import org.openstreetmap.josm.plugins.changeset.util.DataSetChangesetBuilder.BoundedChangesetDataSet; 35 | import org.openstreetmap.josm.tools.GBC; 36 | import org.openstreetmap.josm.tools.ImageProvider; 37 | import org.openstreetmap.josm.tools.OpenBrowser; 38 | import org.openstreetmap.josm.tools.Shortcut; 39 | 40 | /** 41 | * The dialog to choose what changeset to show 42 | * @author ruben 43 | */ 44 | public final class ChangesetDialog extends ToggleDialog implements ActionListener { 45 | private Future buttonUpdater; 46 | 47 | private final MapView mv = MainApplication.getMap().mapView; 48 | private final JButton jButtonNext = new JButton(tr("Next ->")); 49 | private final JButton jButtonprevious = new JButton(tr("<- Previous")); 50 | private final JosmTextField jTextFieldChangesetId; 51 | private final ListCellRenderer renderer = new CellRenderer(); 52 | private final JProgressBar progressBar = new JProgressBar(); 53 | private final JComboBox jComboBox = new JComboBox<>(); 54 | private boolean flag = true; 55 | 56 | /** 57 | * Create a new {@link ChangesetDialog} object 58 | */ 59 | public ChangesetDialog() { 60 | super(tr("Changeset viewer"), "changeset", tr("Open changeset Viewer window."), 61 | Shortcut.registerShortcut("Tool:changeset-viewer", tr("Toggle: {0}", tr("Tool:changeset-Viewer")), 62 | KeyEvent.VK_T, Shortcut.ALT_CTRL_SHIFT), 120); 63 | 64 | JPanel jPanelProjects = new JPanel(new GridBagLayout()); 65 | jPanelProjects.setBorder(BorderFactory.createTitledBorder("")); 66 | JButton jButtonGetChangesets = new JButton(tr("Get changeset in the area")); 67 | jPanelProjects.add(jButtonGetChangesets, GBC.eol().fill(GBC.HORIZONTAL)); 68 | jPanelProjects.add(progressBar, GBC.eol().fill(GBC.HORIZONTAL)); 69 | progressBar.setVisible(false); 70 | progressBar.setIndeterminate(true); 71 | jButtonprevious.setEnabled(false); 72 | jButtonNext.setEnabled(false); 73 | 74 | jButtonGetChangesets.addActionListener((ActionEvent e) -> { 75 | flag = false; 76 | Config.setPAGE(1); 77 | //Get area 78 | Bounds bounds = mv.getRealBounds(); 79 | String bbox = bounds.getMinLon() + "," + bounds.getMinLat() + "," + bounds.getMaxLon() + "," + bounds.getMaxLat(); 80 | Config.setBBOX(bbox); 81 | getChangesets(); 82 | jButtonNext.setEnabled(true); 83 | }); 84 | 85 | jButtonprevious.addActionListener((ActionEvent e) -> { 86 | flag = false; 87 | if (Config.getPAGE() > 1) { 88 | Config.setPAGE(Config.getPAGE() - 1); 89 | getChangesets(); 90 | } 91 | }); 92 | 93 | jButtonNext.addActionListener((ActionEvent e) -> { 94 | jButtonprevious.setEnabled(true); 95 | flag = false; 96 | Config.setPAGE(Config.getPAGE() + 1); 97 | getChangesets(); 98 | }); 99 | 100 | jComboBox.addActionListener(this); 101 | jPanelProjects.add(jComboBox, GBC.eol().fill(GBC.HORIZONTAL)); 102 | jPanelProjects.add(jButtonprevious, GBC.std().fill(GridBagConstraints.HORIZONTAL)); 103 | jPanelProjects.add(jButtonNext, GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 104 | jTextFieldChangesetId = new JosmTextField(); 105 | jTextFieldChangesetId.setHint("55006771"); 106 | jPanelProjects.add(jTextFieldChangesetId, GBC.eol().fill(GBC.HORIZONTAL)); 107 | SideButton displayChangesetButton = new SideButton(new AbstractAction() { 108 | { 109 | putValue(NAME, tr("Display changeset")); 110 | new ImageProvider("mapmode", "getchangeset").getResource().attachImageIcon(this, true); 111 | putValue(SHORT_DESCRIPTION, tr("Display changeset")); 112 | } 113 | 114 | @Override 115 | public void actionPerformed(ActionEvent e) { 116 | if (!jTextFieldChangesetId.getText().isEmpty()) { 117 | printMap(jTextFieldChangesetId.getText()); 118 | } else { 119 | JOptionPane.showMessageDialog(MainApplication.getMainFrame(), tr("Fill a changeset id!")); 120 | } 121 | } 122 | }); 123 | SideButton openChangesetweb = new SideButton(new AbstractAction() { 124 | { 125 | putValue(NAME, tr("Open in OSM")); 126 | new ImageProvider("mapmode", "getchangeset").getResource().attachImageIcon(this, true); 127 | putValue(SHORT_DESCRIPTION, tr("Open in OSM")); 128 | } 129 | 130 | @Override 131 | public void actionPerformed(ActionEvent e) { 132 | if (!jTextFieldChangesetId.getText().isEmpty()) { 133 | OpenBrowser.displayUrl(Config.OSMCHANGESET + jTextFieldChangesetId.getText()); 134 | } else { 135 | JOptionPane.showMessageDialog(MainApplication.getMainFrame(), tr("Fill a changeset id!")); 136 | } 137 | } 138 | }); 139 | createLayout(jPanelProjects, false, Arrays.asList(displayChangesetButton, openChangesetweb)); 140 | } 141 | 142 | @Override 143 | public void actionPerformed(ActionEvent e) { 144 | ChangesetBeen ch = (ChangesetBeen) jComboBox.getSelectedItem(); 145 | if (ch != null && flag) { 146 | jTextFieldChangesetId.setText(String.valueOf(ch.getChangesetId())); 147 | printMap(String.valueOf(ch.getChangesetId())); 148 | } 149 | flag = true; 150 | } 151 | 152 | private synchronized void getChangesets() { 153 | if (this.buttonUpdater != null) { 154 | this.buttonUpdater.cancel(true); 155 | } 156 | this.progressBar.setVisible(true); 157 | this.jComboBox.setVisible(false); 158 | jComboBox.removeAllItems(); 159 | jComboBox.setEnabled(false); 160 | this.buttonUpdater = MainApplication.worker.submit(this::asyncChangesets); 161 | } 162 | 163 | private void asyncChangesets() { 164 | try { 165 | ChangesetBeen[] changesetBeens = ChangesetController.getListChangeset(); 166 | GuiHelper.runInEDT(() -> { 167 | jComboBox.setEnabled(true); 168 | for (ChangesetBeen changesetBeen : changesetBeens) { 169 | if (changesetBeen != null) { 170 | jComboBox.addItem(changesetBeen); 171 | } 172 | } 173 | jComboBox.setRenderer(renderer); 174 | this.progressBar.setVisible(false); 175 | this.jComboBox.setVisible(true); 176 | }); 177 | } finally { 178 | GuiHelper.runInEDT(() -> { 179 | this.progressBar.setVisible(false); 180 | this.jComboBox.setVisible(true); 181 | }); 182 | } 183 | } 184 | 185 | /** 186 | * Show how a changeset modified OSM 187 | * @param changesetId The id to show 188 | */ 189 | public static void printMap(String changesetId) { 190 | BoundedChangesetDataSet boundedDataSet = ChangesetController.getChangeset(changesetId); 191 | if (boundedDataSet == null) { 192 | JOptionPane.showMessageDialog(MainApplication.getMainFrame(), 193 | tr("Check the right changeset Id, if it is ok, maybe the changeset was not processed yet, try again in few minutes!")); 194 | } else { 195 | Changeset.work(boundedDataSet, changesetId); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/org/openstreetmap/josm/plugins/changeset/util/DataSetChangesetBuilder.java: -------------------------------------------------------------------------------- 1 | // License: MIT. For details, see LICENSE file. 2 | package org.openstreetmap.josm.plugins.changeset.util; 3 | 4 | import java.io.StringReader; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.TreeMap; 11 | import java.util.stream.Collectors; 12 | 13 | import jakarta.json.Json; 14 | import jakarta.json.JsonArray; 15 | import jakarta.json.JsonObject; 16 | import jakarta.json.JsonReader; 17 | import jakarta.json.JsonString; 18 | import jakarta.json.JsonValue; 19 | 20 | import org.openstreetmap.josm.data.Bounds; 21 | import org.openstreetmap.josm.data.coor.ILatLon; 22 | import org.openstreetmap.josm.data.coor.LatLon; 23 | import org.openstreetmap.josm.data.osm.DataSet; 24 | import org.openstreetmap.josm.data.osm.Node; 25 | import org.openstreetmap.josm.data.osm.OsmPrimitive; 26 | import org.openstreetmap.josm.data.osm.Way; 27 | import org.openstreetmap.josm.tools.Logging; 28 | 29 | /** 30 | * Build the changeset dataset to show the user 31 | * @author ruben 32 | */ 33 | public class DataSetChangesetBuilder { 34 | 35 | /** 36 | * A bounded changset dataset to show the user 37 | */ 38 | public static class BoundedChangesetDataSet { 39 | 40 | private final DataSet dataSet; 41 | private final Bounds bounds; 42 | 43 | /** 44 | * Create a new {@link BoundedChangesetDataSet} 45 | * @param dataSet The dataset with the changeset data 46 | * @param bounds The bounds of the changeset 47 | */ 48 | public BoundedChangesetDataSet(final DataSet dataSet, final Bounds bounds) { 49 | this.dataSet = dataSet; 50 | this.bounds = bounds; 51 | } 52 | 53 | public Bounds getBounds() { 54 | return this.bounds; 55 | } 56 | 57 | public DataSet getDataSet() { 58 | return this.dataSet; 59 | } 60 | } 61 | 62 | private DataSet dataSet; 63 | 64 | /** 65 | * Build the dataset to show the user 66 | * @param dataString The json string 67 | * @return The dataset 68 | */ 69 | public BoundedChangesetDataSet build(final String dataString) { 70 | dataSet = new DataSet(); 71 | try (JsonReader reader = Json.createReader(new StringReader(dataString))) { 72 | JsonObject jsonObject = reader.readObject(); 73 | return build(jsonObject.getJsonArray("elements")); 74 | } 75 | } 76 | 77 | private BoundedChangesetDataSet build(JsonArray array) { 78 | for (JsonObject obj : array.getValuesAs(JsonObject.class)) { 79 | String action = obj.getString("action"); 80 | String type = obj.getString("type"); 81 | JsonObject tags = obj.getJsonObject("tags"); 82 | //DELETE 83 | if ("delete".equals(action) && "node".equals(type) && !obj.isNull("old")) { 84 | JsonObject old = obj.getJsonObject("old"); 85 | processPoint(tags, old, action); 86 | } else if ("delete".equals(action) && "way".equals(type) && !obj.isNull("old")) { 87 | JsonObject old = obj.getJsonObject("old"); 88 | processLineString(tags, old, action); 89 | } else if ("create".equals(action) && "node".equals(type)) { //CREATE 90 | processPoint(tags, obj, action); 91 | } else if ("create".equals(action) && "way".equals(type)) { 92 | processLineString(tags, obj, action); 93 | } else if ("modify".equals(action) && "way".equals(type)) { //MODIFY 94 | //NEW 95 | processLineString(tags, obj, "modify-new"); 96 | //OLD 97 | JsonObject old = obj.getJsonObject("old"); 98 | processLineString(tags, old, "modify-old"); 99 | } else if ("modify".equals(action) && "node".equals(type)) { 100 | //NEW 101 | processPoint(tags, obj, "modify-new"); 102 | //OLD 103 | JsonObject old = obj.getJsonObject("old"); 104 | processPoint(tags, old, "modify-old"); 105 | //RELATION 106 | } else if ("modify".equals(action) && "relation".equals(type)) { 107 | //OLD 108 | JsonObject old = obj.getJsonObject("old"); 109 | Bounds boundsRelationOld = buildRelation(old); 110 | bounds2rectangle(tags, boundsRelationOld, "modify-old-rel"); 111 | //NEW 112 | Bounds boundsRelationNew = buildRelation(obj); 113 | bounds2rectangle(tags, boundsRelationNew, "modify-new-rel"); 114 | } else if ("create".equals(action) && "relation".equals(type)) { 115 | Bounds boundsRelationNew = buildRelation(obj); 116 | bounds2rectangle(tags, boundsRelationNew, "create-rel"); 117 | } else if ("delete".equals(action) && "relation".equals(type)) { 118 | JsonObject old = obj.getJsonObject("old"); 119 | Bounds boundsRelationNew = buildRelation(old); 120 | bounds2rectangle(tags, boundsRelationNew, "delete-rel"); 121 | } 122 | } 123 | 124 | Bounds bounds = null; 125 | for (OsmPrimitive osmPrimitive : dataSet.allPrimitives()) { 126 | bounds = mergeBounds(bounds, osmPrimitive); 127 | } 128 | return new BoundedChangesetDataSet(dataSet, bounds); 129 | } 130 | 131 | private void processPoint(final JsonObject tags, final JsonObject nodeJson, final String action) { 132 | final Node node = createNode(newLatLon(nodeJson)); 133 | fillTagsFromFeature(tags, node, action); 134 | } 135 | 136 | private void processLineString(final JsonObject tags, final JsonObject wayJson, final String action) { 137 | JsonArray arrayNodes = wayJson.getJsonArray("nodes"); 138 | if (arrayNodes.isEmpty()) { 139 | return; 140 | } 141 | 142 | List coordinates = new LinkedList<>(); 143 | for (int i = 0; i < arrayNodes.size(); i++) { 144 | coordinates.add(newLatLon(arrayNodes.getJsonObject(i))); 145 | } 146 | 147 | final Way way = createWay(coordinates); 148 | fillTagsFromFeature(tags, way, action); 149 | } 150 | 151 | private static void fillTagsFromFeature(final JsonObject tags, final OsmPrimitive primitive, final String action) { 152 | if (tags != null) { 153 | primitive.setKeys(getTags(tags, action)); 154 | } 155 | } 156 | 157 | private Node createNode(final LatLon latLon) { 158 | final Node node = new Node(latLon); 159 | dataSet.addPrimitive(node); 160 | return node; 161 | } 162 | 163 | private Way createWay(final List coordinates) { 164 | if (coordinates.isEmpty()) { 165 | return null; 166 | } 167 | final Way way = new Way(); 168 | way.setNodes(coordinates.stream().map(this::createNode).collect(Collectors.toList())); 169 | dataSet.addPrimitive(way); 170 | return way; 171 | } 172 | 173 | private static Map getTags(final JsonObject tags, final String action) { 174 | final Map mapTags = new TreeMap<>(); 175 | mapTags.put("action", action); 176 | for (Map.Entry entry : tags.entrySet()) { 177 | mapTags.put(entry.getKey(), String.valueOf(entry.getValue().toString())); 178 | } 179 | return mapTags; 180 | } 181 | 182 | private static Bounds mergeBounds(final Bounds bounds, final OsmPrimitive osmPrimitive) { 183 | // ways and relations consist of nodes that are already in the dataset 184 | if (osmPrimitive instanceof Node && ((Node) osmPrimitive).isLatLonKnown()) { 185 | return mergeBounds(bounds, ((ILatLon) osmPrimitive)); 186 | } 187 | return bounds; 188 | } 189 | 190 | private static Bounds mergeBounds(final Bounds bounds, final ILatLon coords) { 191 | if (bounds == null) { 192 | return new Bounds(coords.lat(), coords.lon(), coords.lat(), coords.lon()); 193 | } else { 194 | bounds.extend(coords.lat(), coords.lon()); 195 | return bounds; 196 | } 197 | } 198 | 199 | private void bounds2rectangle(final JsonObject tags, final Bounds bounds, final String action) { 200 | if (bounds == null) { 201 | return; 202 | } 203 | double minLat = bounds.getMinLat(); 204 | double minLon = bounds.getMinLon(); 205 | double maxLat = bounds.getMaxLat(); 206 | double maxLon = bounds.getMaxLon(); 207 | List nodes = Arrays.asList( 208 | new Node(new LatLon(minLat, minLon)), 209 | new Node(new LatLon(minLat, maxLon)), 210 | new Node(new LatLon(maxLat, maxLon)), 211 | new Node(new LatLon(maxLat, minLon)), 212 | new Node(new LatLon(minLat, minLon)) 213 | ); 214 | Way way = new Way(); 215 | way.setNodes(nodes); 216 | fillTagsFromFeature(tags, way, action); 217 | dataSet.addPrimitiveRecursive(way); 218 | } 219 | 220 | private static Bounds buildRelation(final JsonObject obj) { 221 | DataSet dataSetRel = new DataSet(); 222 | JsonArray members = obj.getJsonArray("members"); 223 | for (int j = 0; j < members.size(); j++) { 224 | JsonObject member = members.getJsonObject(j); 225 | String memberType = member.getString("type"); 226 | if ("way".equals(memberType)) { 227 | dataSetRel.addPrimitive(processRelationLineString(member, dataSetRel)); 228 | } else if ("node".equals(memberType)) { 229 | dataSetRel.addPrimitive(newNode(member)); 230 | } 231 | } 232 | Bounds boundsRelationOld = null; 233 | for (OsmPrimitive osmPrimitive : dataSetRel.allPrimitives()) { 234 | boundsRelationOld = mergeBounds(boundsRelationOld, osmPrimitive); 235 | } 236 | return boundsRelationOld; 237 | } 238 | 239 | private static LatLon newLatLon(final JsonObject json) { 240 | JsonString latString = json.getJsonString("lat"); 241 | JsonString lonString = json.getJsonString("lon"); 242 | if (latString == null || lonString == null) { 243 | Logging.error("Invalid JSON: " + json); 244 | return null; 245 | } 246 | return new LatLon( 247 | Double.parseDouble(latString.getString()), 248 | Double.parseDouble(lonString.getString())); 249 | } 250 | 251 | private static Node newNode(final JsonObject nodeJson) { 252 | return new Node(newLatLon(nodeJson)); 253 | } 254 | 255 | private static Way processRelationLineString(final JsonObject wayJson, DataSet dataSetOld) { 256 | JsonArray arrayNodes = wayJson.getJsonArray("nodes"); 257 | Way way = new Way(); 258 | if (arrayNodes.isEmpty()) { 259 | return way; 260 | } 261 | List nodes = new ArrayList<>(arrayNodes.size()); 262 | for (int i = 0; i < arrayNodes.size(); i++) { 263 | Node node = newNode(arrayNodes.getJsonObject(i)); 264 | dataSetOld.addPrimitive(node); 265 | nodes.add(node); 266 | } 267 | way.setNodes(nodes); 268 | return way; 269 | } 270 | } 271 | --------------------------------------------------------------------------------