use image size in conjunction with "stage" URL marker to determine stage image

This commit is contained in:
Axel Uhl committed 2014-08-25 23:31:24 +02:00
1 parent 1b46a87d79
commit 7dc02f6540
12 files changed
+146 -53

No files matched your search

@@ -0,0 +1,8 @@
package com.sap.sailing.domain.common;
import java.io.Serializable;
public interface ImageSize extends Serializable {
int getWidth();
int getHeight();
}
@@ -0,0 +1,28 @@
package com.sap.sailing.domain.common.impl;
import com.sap.sailing.domain.common.ImageSize;
public class ImageSizeImpl implements ImageSize {
private static final long serialVersionUID = 1170701774852068780L;
private int width;
private int height;
ImageSizeImpl() {} // for GWT serialization
public ImageSizeImpl(int width, int height) {
super();
this.width = width;
this.height = height;
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
@Override
public String toString() {
return "("+getWidth()+"x"+getHeight()+")";
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.base;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import com.sap.sailing.domain.common.ImageSize;
import com.sap.sailing.domain.common.Renamable;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WithID;
@@ -121,11 +122,6 @@ public interface EventBase extends Named, WithDescription, Renamable, WithID {
Iterable<? extends LeaderboardGroupBase> getLeaderboardGroups();
public static interface ImageSize {
int getWidth();
int getHeight();
}
/**
* For the images references by the image URLs in {@link #getImageURLs()}, {@link #getSponsorImageURLs()} and {@link #getLogoImageURL()}
* determines the image dimensions.
@@ -221,26 +221,4 @@ public abstract class EventBaseImpl implements EventBase {
this.officialWebsiteURL = officialWebsiteURL;
}
public static class ImageSizeImpl implements ImageSize {
private final int width;
private final int height;
public ImageSizeImpl(int width, int height) {
super();
this.width = width;
this.height = height;
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
@Override
public String toString() {
return "("+getWidth()+"x"+getHeight()+")";
}
}
}
@@ -9,6 +9,7 @@ import java.util.concurrent.ExecutionException;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.Venue;
import com.sap.sailing.domain.common.ImageSize;
import com.sap.sailing.domain.common.TimePoint;
/**
@@ -33,7 +34,7 @@ public class StrippedEventImpl extends EventBaseImpl {
boolean isPublic, UUID id, Iterable<LeaderboardGroupBase> leaderboardGroups) {
super(name, startDate, endDate, venue, isPublic, id);
this.leaderboardGroups = leaderboardGroups;
this.imageSizes = new HashMap<URL, EventBase.ImageSize>();
this.imageSizes = new HashMap<URL, ImageSize>();
}
@Override
@@ -4,6 +4,7 @@ import java.net.URL;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import com.sap.sailing.domain.common.ImageSize;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
/**
@@ -24,7 +24,9 @@ import javax.imageio.stream.ImageInputStream;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Venue;
import com.sap.sailing.domain.common.ImageSize;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.ImageSizeImpl;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sse.common.Util;
@@ -1,15 +1,14 @@
package com.sap.sailing.gwt.home.client.place.start;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import com.google.gwt.activity.shared.AbstractActivity;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.sap.sailing.gwt.home.client.shared.placeholder.Placeholder;
@@ -50,9 +49,8 @@ public class StartActivity extends AbstractActivity {
List<EventBaseDTO> upcomingSoonEvents = new ArrayList<EventBaseDTO>();
List<EventBaseDTO> popularEvents = new ArrayList<EventBaseDTO>();
Date now = new Date();
Calendar cal = new GregorianCalendar();
cal.setTime(now);
int currentYear = cal.get(Calendar.YEAR);
DateTimeFormat yearFetcher = DateTimeFormat.getFormat("yyyy");
String currentYear = yearFetcher.format(now);
final int MAX_STAGE_EVENTS = 10;
final long FOUR_WEEK_IN_MS = 4L * (1000 * 60 * 60 * 24 * 7);
@@ -62,11 +60,8 @@ public class StartActivity extends AbstractActivity {
featuredEvents.add(new Pair<StageEventType, EventBaseDTO>(StageEventType.RUNNING, event));
} else if (event.startDate.after(now) && event.startDate.getTime() - now.getTime() < FOUR_WEEK_IN_MS) {
upcomingSoonEvents.add(event);
} else {
cal.setTime(event.endDate);
if (event.endDate.before(now) && cal.get(Calendar.YEAR) == currentYear) {
recentEventsOfSameYear.add(event);
}
} else if (event.endDate.before(now) && yearFetcher.format(event.endDate).equals(currentYear)) {
recentEventsOfSameYear.add(event);
}
}
}
@@ -3271,17 +3271,32 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
eventDTO.id = (UUID) event.getId();
eventDTO.setDescription(event.getDescription());
eventDTO.setOfficialWebsiteURL(event.getOfficialWebsiteURL() != null ? event.getOfficialWebsiteURL().toString() : null);
eventDTO.setLogoImageURL(event.getLogoImageURL() != null ? event.getLogoImageURL().toString() : null);
if (event.getLogoImageURL() == null) {
eventDTO.setLogoImageURL(null);
} else {
eventDTO.setLogoImageURL(event.getLogoImageURL().toString());
setImageSize(event, eventDTO, event.getLogoImageURL());
}
for(URL url: event.getSponsorImageURLs()) {
eventDTO.addSponsorImageURL(url.toString());
setImageSize(event, eventDTO, url);
}
for(URL url: event.getImageURLs()) {
eventDTO.addImageURL(url.toString());
setImageSize(event, eventDTO, url);
}
for(URL url: event.getVideoURLs()) {
eventDTO.addVideoURL(url.toString());
}
}
private void setImageSize(EventBase event, EventBaseDTO eventDTO, URL imageURL) {
try {
eventDTO.setImageSize(imageURL.toString(), event.getImageSize(imageURL));
} catch (InterruptedException | ExecutionException e) {
logger.log(Level.FINE, "Was unable to obtain image size for "+imageURL+" earlier.", e);
}
}
private EventDTO convertToEventDTO(Event event, boolean withStatisticalData) {
EventDTO eventDTO = new EventDTO(event.getName());
@@ -1,15 +1,22 @@
package com.sap.sailing.gwt.ui.shared;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sailing.domain.common.ImageSize;
import com.sap.sailing.domain.common.dto.NamedDTO;
import com.sap.sse.common.Util;
public class EventBaseDTO extends NamedDTO implements IsSerializable {
private static final long serialVersionUID = 818666323178097939L;
private static final String STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE = "stage";
public VenueDTO venue;
public Date startDate;
public Date endDate;
@@ -26,6 +33,11 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable {
/** placeholder for social media URL's -> attributes will be implemented later on */
private String facebookURL;
private String twitterURL;
/**
* For the image URL keys holds the sizes of these images if known. An image size is "known" by this object if it was
* provided to the {@link #setImageSize} method.
*/
private Map<String, ImageSize> imageSizes;
/**
* The base URL for the server instance on which the data for this event can be reached. Could be something like
@@ -45,11 +57,13 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable {
public EventBaseDTO(List<? extends LeaderboardGroupBaseDTO> leaderboardGroups) {
this.leaderboardGroups = leaderboardGroups;
this.imageSizes = new HashMap<String, ImageSize>();
}
public EventBaseDTO(String name, List<? extends LeaderboardGroupBaseDTO> leaderboardGroups) {
super(name);
this.leaderboardGroups = leaderboardGroups;
this.imageSizes = new HashMap<String, ImageSize>();
}
public boolean isRunning() {
@@ -113,24 +127,63 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable {
return imageURLs;
}
/**
* The stage image is determined from the {@link #imageURLs} collection by a series of heuristics and fall-back rules:
* <ol>
* <li>If one or more image URLs has "stage" (ignoring case) in its name, only they are considered candidates.</li>
* <li>If no image URL has "stage" (ignoring case) in its name, all images from {@link #imageURLs} are considered candidates.</li>
* <li>From all candidates, the one with the biggest known size (determined by the product of width and height) is chosen.</li>
* <li>If the size isn't known for any candidate, the first candidate in {@link #imageURLs} is picked.</li>
* </ol>
*/
public String getStageImageURL() {
String result = null;
for(String imageUrl: imageURLs) {
// FIXME consolidate with getPhotoGalleryImageURLs, removing redundant "STAGE" code
// TODO use image size to find largest image(s) in case none is explicitly marked with "state" in its URL
if(imageUrl.contains("stage") || imageUrl.contains("STAGE")) {
result = imageUrl;
break;
}
final String result;
if (imageURLs.isEmpty()) {
result = null;
} else {
Comparator<String> stageImageComparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
final int result;
if (o1.toLowerCase().contains(STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) {
if (o2.toLowerCase().contains(STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) {
result = compareBySize(o1, o2);
} else {
// o1 has stage indicator substring in its URL but o2 doesn't; o1 ranks greater
result = 1;
}
} else {
if (o2.toLowerCase().contains(STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) {
result = -1; // o1 does not and o2 does have stage indicator substring, so o2 ranks greater
} else {
// both don't have stage indicator; compare by size
result = compareBySize(o1, o2);
}
}
return result;
}
private int compareBySize(String o1, String o2) {
final int result;
final ImageSize o1Size = getImageSize(o1);
final ImageSize o2Size = getImageSize(o2);
result = (o1Size == null ? 0 : (o1Size.getWidth() * o1Size.getHeight()))
- (o2Size == null ? 0 : (o2Size.getWidth() * o2Size.getHeight()));
return result;
}
};
List<String> sortedImageURLs = new ArrayList<>(imageURLs);
Collections.sort(sortedImageURLs, stageImageComparator);
result = sortedImageURLs.get(sortedImageURLs.size() - 1);
}
return result;
}
public List<String> getPhotoGalleryImageURLs() {
String stageImageURL = getStageImageURL(); // if set, exclude stage image from photo gallery
List<String> result = new ArrayList<String>();
for(String imageUrl: imageURLs) {
// FIXME consolidate with getStageImageURL, removing redundant "STAGE" code
if(!imageUrl.contains("stage") && !imageUrl.contains("STAGE")) {
for (String imageUrl : imageURLs) {
if (!Util.equalsWithNull(imageUrl, stageImageURL)) {
result.add(imageUrl);
}
}
@@ -172,5 +225,21 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable {
public void setTwitterURL(String twitterURL) {
this.twitterURL = twitterURL;
}
public void setImageSize(String imageURL, ImageSize imageSize) {
if (imageSize == null) {
imageSizes.remove(imageURL);
} else {
imageSizes.put(imageURL, imageSize);
}
}
/**
* @return the size of the image referenced by <code>imageURL</code> or <code>null</code> if that size is not known
* of the image URL is none of those known to this event, in particular neither of {@link #getImageURLs()}
* or {@link #getSponsorImageURLs()}.
*/
public ImageSize getImageSize(String imageURL) {
return imageSizes.get(imageURL);
}
}
@@ -12,8 +12,8 @@ import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.Venue;
import com.sap.sailing.domain.base.impl.EventBaseImpl;
import com.sap.sailing.domain.base.impl.StrippedEventImpl;
import com.sap.sailing.domain.common.impl.ImageSizeImpl;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
@@ -95,7 +95,7 @@ public class EventBaseJsonDeserializer implements JsonDeserializer<EventBase> {
try {
result.setImageSize(
new URL((String) imageURLAndSizeJson.get(EventBaseJsonSerializer.FIELD_IMAGE_URL)),
new EventBaseImpl.ImageSizeImpl(
new ImageSizeImpl(
((Number) imageURLAndSizeJson.get(EventBaseJsonSerializer.FIELD_IMAGE_WIDTH)).intValue(),
((Number) imageURLAndSizeJson.get(EventBaseJsonSerializer.FIELD_IMAGE_HEIGHT)).intValue()));
} catch (MalformedURLException e) {
@@ -9,9 +9,9 @@ import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.EventBase.ImageSize;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.Venue;
import com.sap.sailing.domain.common.ImageSize;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public class EventBaseJsonSerializer implements JsonSerializer<EventBase> {