Skip to content

Commit

Permalink
Add user information to image window and image info
Browse files Browse the repository at this point in the history
Signed-off-by: Taylor Smock <[email protected]>
  • Loading branch information
tsmock committed Nov 6, 2023
1 parent 1e5815c commit a509bcf
Show file tree
Hide file tree
Showing 20 changed files with 316 additions and 294 deletions.
2 changes: 2 additions & 0 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<property file="${basedir}/gradle.properties"/>
<property name="josm" location="../../core/dist/josm-custom.jar"/>
<property name="plugin.dist.dir" value="../../dist"/>
<property name="java.lang.version" value="17"/>
<property name="plugin.minimum.java.version" value="${java.lang.version}"/>
<!-- ** include targets that all plugins have in common ** -->
<import file="../build-common.xml"/>
<target name="pre-compile" depends="fetch_dependencies,mapillary_secrets">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,34 +33,27 @@
/**
* Record organization information
*
* @param id the unique key for the organization
* @param name the machine-readable name for an organization
* @param niceName the human-readable name for an organization
* @param description the description for the organization
* @param avatar the avatar for the organization
* @author Taylor Smock
*/
// @Immutable
public final class OrganizationRecord implements Serializable {
public record OrganizationRecord(long id, String name, String niceName, String description, ImageIcon avatar)
implements Serializable {

private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+");
private static final ListenerList<OrganizationRecordListener> LISTENERS = ListenerList.create();
private final String description;
private final long id;
private final String name;
private final String niceName;
private final ImageIcon avatar;

private static final Map<Long, OrganizationRecord> CACHE = new ConcurrentHashMap<>(1);

public static final OrganizationRecord NULL_RECORD = new OrganizationRecord(0L, "", "", "", "");
public static final OrganizationRecord NULL_RECORD = new OrganizationRecord(0L, "", "", "", createAvatarIcon(""));

static {
CACHE.put(0L, NULL_RECORD);
}

private OrganizationRecord(Long id, String slug, String name, String description, String avatarUrl) {
this.avatar = createAvatarIcon(avatarUrl);
this.description = description;
this.id = id;
this.name = slug;
this.niceName = name;
}

/**
* Get or create an avatar icon
*
Expand All @@ -84,9 +77,9 @@ private static ImageIcon createAvatarIcon(@Nullable String avatar) {
*/
private static BufferedImage fetchAvatarIcon(String url) {
try {
HttpClient client = HttpClient.create(URI.create(url).toURL());
final var client = HttpClient.create(URI.create(url).toURL());
OAuthUtils.addAuthenticationHeader(client);
HttpClient.Response response = client.connect();
final var response = client.connect();
if (response.getResponseCode() >= 200 && response.getResponseCode() < 400) {
return ImageIO.read(response.getContent());
}
Expand Down Expand Up @@ -126,8 +119,8 @@ private static OrganizationRecord getNewOrganization(long id) {
// TODO check for API in v4 (preferably one that doesn't need user auth)
final String url = MapillaryConfig.getUrls().getOrganizationInformation(id);
try {
final JsonObject data = OAuthUtils.getWithHeader(URI.create(url));
final OrganizationRecord organizationRecord = decodeNewOrganization(data);
final var data = OAuthUtils.getWithHeader(URI.create(url));
final var organizationRecord = decodeNewOrganization(data);
// Ensure that we aren't blocking the main EDT thread
MainApplication.worker.execute(() -> LISTENERS.fireEvent(l -> l.organizationAdded(organizationRecord)));
return organizationRecord;
Expand All @@ -144,16 +137,16 @@ private static OrganizationRecord getNewOrganization(long id) {
* @return A new organization record
*/
private static OrganizationRecord decodeNewOrganization(JsonObject organization) {
String description = organization.getString("description", "");
String slug = organization.getString("slug", "");
String name = organization.getString("name", "");
String idString = organization.getString("id", "");
String avatarUrl = organization.getString("profile_photo_url", null);
final var description = organization.getString("description", "");
final var slug = organization.getString("slug", "");
final var name = organization.getString("name", "");
final var idString = organization.getString("id", "");
final var avatarUrl = organization.getString("profile_photo_url", null);
long id = 0;
if (NUMBER_PATTERN.matcher(idString).matches()) {
id = Long.parseLong(idString);
}
return new OrganizationRecord(id, slug, name, description, avatarUrl);
return new OrganizationRecord(id, slug, name, description, createAvatarIcon(avatarUrl));
}

/**
Expand All @@ -166,52 +159,6 @@ public static void addFromTile(MVTTile tile) {
.forEach(MapillaryImageUtils::getOrganization);
}

/**
* Get the avatar for the organization
*
* @return The avatar for the organization
*/
public ImageIcon getAvatar() {
return avatar != null ? avatar : ImageProvider.createBlankIcon(ImageSizes.DEFAULT);
}

/**
* Get the description for the organization
*
* @return The organization description
*/
public String getDescription() {
return description;
}

/**
* Get the unique key for the organization
*
* @return The organization key
*/
public long getId() {
return id;
}

/**
* Get the machine-readable name for an organization
*
* @return The name of the organization
* @see OrganizationRecord#getNiceName
*/
public String getName() {
return name;
}

/**
* Get the human-readable name for an organization
*
* @return The nice-looking name of the organization
*/
public String getNiceName() {
return niceName;
}

/**
* Add listener for organizations
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
import java.io.Serial;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.locks.Lock;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
Expand All @@ -34,7 +33,6 @@
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;

import jakarta.annotation.Nonnull;
Expand Down Expand Up @@ -76,6 +74,7 @@
public final class MapillaryFilterDialog extends ToggleDialog
implements OrganizationRecordListener, MVTTile.TileListener {

@Serial
private static final long serialVersionUID = -4192029663670922103L;

private static MapillaryFilterDialog instance;
Expand Down Expand Up @@ -108,9 +107,9 @@ private MapillaryFilterDialog() {
Shortcut.NONE),
200, false, MapillaryPreferenceSetting.class);

final JPanel panel = new JPanel(new GridBagLayout());
final var panel = new JPanel(new GridBagLayout());
panel.add(new JLabel(tr("Picture Filters")), GBC.eol().anchor(GridBagConstraints.LINE_START));
final JPanel imageLine = new JPanel();
final var imageLine = new JPanel();
panel.add(imageLine, GBC.eol().anchor(GridBagConstraints.LINE_START));
this.addTimeFilters(panel);
this.addUserGroupFilters(panel);
Expand All @@ -120,7 +119,7 @@ private MapillaryFilterDialog() {
panel.add(imageTypes, GBC.eol().anchor(GridBagConstraints.LINE_START));

panel.add(new JSeparator(), GBC.eol().fill(GridBagConstraints.HORIZONTAL));
final TrafficSignFilter objectFilter = new TrafficSignFilter();
final var objectFilter = new TrafficSignFilter();
panel.add(new JLabel(tr("Object Detection Filters")),
GBC.eol().anchor(GridBagConstraints.WEST).fill(GridBagConstraints.HORIZONTAL));
this.destroyable.addListener(objectFilter);
Expand Down Expand Up @@ -148,7 +147,7 @@ private MapillaryFilterDialog() {
* @param panel The panel to add the filters to
*/
private void addUserGroupFilters(JPanel panel) {
final JPanel userSearchPanel = new JPanel();
final var userSearchPanel = new JPanel();
userSearchPanel.setLayout(new FlowLayout(FlowLayout.LEFT));

organizationLabel.setToolTipText(tr("Organizations"));
Expand Down Expand Up @@ -181,23 +180,23 @@ private void addUserGroupFilters(JPanel panel) {
*/
private void addTimeFilters(JPanel panel) {
// Time from panel
final JPanel fromPanel = new JPanel();
final var fromPanel = new JPanel();
fromPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
final JCheckBox filterByDateCheckbox = new JCheckBox(tr("Not older than: "));
final var filterByDateCheckbox = new JCheckBox(tr("Not older than: "));
fromPanel.add(filterByDateCheckbox);
final SpinnerNumberModel spinnerModel = new SpinnerNumberModel(1.0, 0, 10000, .1);
final JSpinner spinner = new DisableShortcutsOnFocusGainedJSpinner(spinnerModel);
final var spinnerModel = new SpinnerNumberModel(1.0, 0, 10000, .1);
final var spinner = new DisableShortcutsOnFocusGainedJSpinner(spinnerModel);
// Set the editor such that we aren't zooming all over the place.
spinner.setEnabled(false);
fromPanel.add(spinner);

final JComboBox<String> time = new JComboBox<>(TIME_LIST);
final var time = new JComboBox<>(TIME_LIST);
time.setEnabled(false);
fromPanel.add(time);

panel.add(fromPanel, GBC.eol().anchor(GridBagConstraints.LINE_START));
// Time panel
final JPanel timePanel = new JPanel(new GridBagLayout());
final var timePanel = new JPanel(new GridBagLayout());
startDate = IDatePicker.getNewDatePicker();
endDate = IDatePicker.getNewDatePicker();
final Consumer<IDatePicker<?>> function = modified -> updateDates(startDate, endDate, modified);
Expand Down Expand Up @@ -299,18 +298,18 @@ public void setEndDate(Instant end) {
* @param organization The organization to filter on
*/
public void setOrganization(String organization) {
OrganizationRecord organizationRecord = OrganizationRecord.getOrganization(organization);
final var organizationRecord = OrganizationRecord.getOrganization(organization);
this.organizations.setSelectedItem(organizationRecord);
}

@Nonnull
private static Instant convertDateRangeBox(@Nonnull SpinnerNumberModel spinner,
@Nonnull JComboBox<String> timeStep) {
if (timeStep.isEnabled()) {
ZonedDateTime current = LocalDate.now(ZoneOffset.UTC).atStartOfDay(ZoneOffset.UTC);
String type = (String) timeStep.getSelectedItem();
Number start = spinner.getNumber();
int[] difference = new int[] { 0, 0, 0 }; // Year, Month, Day
final var current = LocalDate.now(ZoneOffset.UTC).atStartOfDay(ZoneOffset.UTC);
final var type = (String) timeStep.getSelectedItem();
final var start = spinner.getNumber();
final var difference = new int[] { 0, 0, 0 }; // Year, Month, Day
if (TIME_LIST[0].equals(type)) {
difference[0] = start.intValue();
difference[1] = (int) ((start.floatValue() - difference[0]) * 12);
Expand All @@ -328,8 +327,8 @@ private static Instant convertDateRangeBox(@Nonnull SpinnerNumberModel spinner,
}

private static void updateDates(IDatePicker<?> startDate, IDatePicker<?> endDate, IDatePicker<?> modified) {
Instant start = startDate.getInstant();
Instant end = endDate.getInstant();
final var start = startDate.getInstant();
final var end = endDate.getInstant();
if (Instant.MIN.equals(start) || Instant.MIN.equals(end)) {
return;
}
Expand Down Expand Up @@ -388,7 +387,7 @@ public synchronized void refresh() {
public void updateFilteredImages() {
if (MapillaryLayer.hasInstance()) {
MainApplication.worker.execute(() -> {
final Lock readLock = MapillaryLayer.getInstance().getData().getReadLock();
final var readLock = MapillaryLayer.getInstance().getData().getReadLock();
try {
readLock.lockInterruptibly();
this.updateFilteredImages(MapillaryLayer.getInstance().getData().getNodes());
Expand Down Expand Up @@ -485,7 +484,7 @@ public boolean test(INode img, Collection<IPrimitive> currentSelection) {
// Filter on organizations
return !OrganizationRecord.NULL_RECORD.equals(this.organization)
&& MapillaryImageUtils.getSequenceKey(img) != null
&& this.organization.getId() != MapillaryImageUtils.getOrganization(img).getId();
&& this.organization.id() != MapillaryImageUtils.getOrganization(img).id();
}
return false;
}
Expand All @@ -505,9 +504,9 @@ private boolean checkStartDate(INode img) {
if (Instant.MIN.equals(startDateRefresh)) {
return false;
}
final Instant start = LocalDateTime.ofInstant(startDateRefresh, ZoneOffset.UTC).toLocalDate()
final var start = LocalDateTime.ofInstant(startDateRefresh, ZoneOffset.UTC).toLocalDate()
.atStartOfDay(ZoneOffset.UTC).toInstant();
final Instant imgDate = MapillaryImageUtils.getDate(img);
final var imgDate = MapillaryImageUtils.getDate(img);
return start.isAfter(imgDate);
}

Expand All @@ -519,10 +518,10 @@ private boolean checkEndDate(INode img) {
if (Instant.MIN.equals(endDateRefresh)) {
return false;
}
final ZonedDateTime nextDate = LocalDateTime.ofInstant(endDateRefresh, ZoneOffset.UTC).toLocalDate()
final var nextDate = LocalDateTime.ofInstant(endDateRefresh, ZoneOffset.UTC).toLocalDate()
.atStartOfDay(ZoneOffset.UTC).plus(1, ChronoUnit.DAYS);
final Instant end = nextDate.toInstant();
final Instant imgDate = MapillaryImageUtils.getDate(img);
final var end = nextDate.toInstant();
final var imgDate = MapillaryImageUtils.getDate(img);
return end.isBefore(imgDate);
}
}
Expand All @@ -536,6 +535,7 @@ public static synchronized void destroyInstance() {

private static class UpdateAction extends AbstractAction {

@Serial
private static final long serialVersionUID = -7417238601979689863L;

UpdateAction() {
Expand All @@ -550,6 +550,7 @@ public void actionPerformed(ActionEvent arg0) {
}

private static class ResetAction extends AbstractAction {
@Serial
private static final long serialVersionUID = 1178261778165525040L;

ResetAction() {
Expand Down Expand Up @@ -579,8 +580,8 @@ public void destroy() {

@Override
public void organizationAdded(OrganizationRecord organization) {
boolean add = true;
for (int i = 0; i < organizations.getItemCount(); i++) {
var add = true;
for (var i = 0; i < organizations.getItemCount(); i++) {
if (organizations.getItemAt(i).equals(organization)) {
add = false;
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.gui.dialog;

import java.awt.Component;
Expand Down Expand Up @@ -27,13 +28,13 @@ public Component getListCellRendererComponent(JList<?> list, Object value, int i
JLabel comp = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof OrganizationRecord) {
OrganizationRecord organization = (OrganizationRecord) value;
if ((organization.getNiceName() != null && !organization.getNiceName().isEmpty())
if ((organization.niceName() != null && !organization.niceName().isEmpty())
|| OrganizationRecord.NULL_RECORD.equals(organization)) {
comp.setText(organization.getNiceName());
comp.setText(organization.niceName());
} else {
comp.setText(Long.toString(organization.getId()));
comp.setText(Long.toString(organization.id()));
}
if (organization.getAvatar() != null) {
if (organization.avatar() != null) {
comp.setIcon(ORGANIZATION_SCALED_ICONS.computeIfAbsent(organization,
OrganizationListCellRenderer::scaleOrganizationIcon));
}
Expand All @@ -49,7 +50,7 @@ public Component getListCellRendererComponent(JList<?> list, Object value, int i
*/
private static ImageIcon scaleOrganizationIcon(final OrganizationRecord organization) {
final ImageProvider.ImageSizes size = ImageProvider.ImageSizes.DEFAULT;
final Image scaledImage = organization.getAvatar().getImage().getScaledInstance(size.getAdjustedWidth(),
final Image scaledImage = organization.avatar().getImage().getScaledInstance(size.getAdjustedWidth(),
size.getAdjustedHeight(), Image.SCALE_SMOOTH);
return new ImageIcon(scaledImage);
}
Expand Down
Loading

0 comments on commit a509bcf

Please sign in to comment.