diff --git a/common-lib/src/main/scala/com/gu/mediaservice/model/Crop.scala b/common-lib/src/main/scala/com/gu/mediaservice/model/Crop.scala index 879ddd4c17..c998ab4617 100644 --- a/common-lib/src/main/scala/com/gu/mediaservice/model/Crop.scala +++ b/common-lib/src/main/scala/com/gu/mediaservice/model/Crop.scala @@ -12,10 +12,25 @@ case class Crop(id: Option[String], author: Option[String], date: Option[DateTim object Crop { import com.gu.mediaservice.lib.formatting._ - def getCropId(b: Bounds) = List(b.x, b.y, b.width, b.height).mkString("_") - - def createFromCropSource(by: Option[String], timeRequested: Option[DateTime], specification: CropSpec, master: Option[Asset] = None, cropSizings: List[Asset] = Nil): Crop = - Crop(Some(getCropId(specification.bounds)), by, timeRequested, specification, master, cropSizings) + def getCropId(b: Bounds): String = List(b.x, b.y, b.width, b.height).mkString("_") + private def getCropId(b: Bounds, fullDimensions: Dimensions): String = s"${getCropId(b)}_${fullDimensions.width}_${fullDimensions.height}" + + def createFromCropSource(by: Option[String], timeRequested: Option[DateTime], specification: CropSpec, master: Option[Asset] = None, cropSizings: List[Asset] = Nil, maybeFullDimensions: Option[Dimensions] = None): Crop = { + Crop( + id = Some(maybeFullDimensions match { + case Some(fullDimensions) => getCropId(specification.bounds, fullDimensions) + case None => getCropId(specification.bounds) + }), + by, + timeRequested, + specification = (specification.`type`, maybeFullDimensions) match { + case (PointsOfInterestExport, Some(fullDimensions)) => specification.copy(bounds = Bounds(0, 0, fullDimensions.width, fullDimensions.height)) + case _ => specification + }, + master, + cropSizings + ) + } def createFromCrop(crop: Crop, master: Asset, assets: List[Asset]): Crop = Crop(crop.id, crop.author, crop.date, crop.specification, Some(master), assets) @@ -43,6 +58,7 @@ object Crop { sealed trait ExportType { val name: String } case object CropExport extends ExportType { val name = "crop" } case object FullExport extends ExportType { val name = "full" } +case object PointsOfInterestExport extends ExportType { val name = "poi" } object ExportType { @@ -51,6 +67,7 @@ object ExportType { def valueOf(name: String): ExportType = name match { case "crop" => CropExport case "full" => FullExport + case "poi" => PointsOfInterestExport } implicit val exportTypeWrites: Writes[ExportType] = Writes[ExportType](t => JsString(t.name)) diff --git a/cropper/app/controllers/CropperController.scala b/cropper/app/controllers/CropperController.scala index 2830948f3c..6951147140 100644 --- a/cropper/app/controllers/CropperController.scala +++ b/cropper/app/controllers/CropperController.scala @@ -173,9 +173,10 @@ class CropperController(auth: Authentication, crops: Crops, store: CropStore, no crop = Crop.createFromCropSource( by = Some(Authentication.getIdentity(user)), timeRequested = Some(new DateTime()), - specification = cropSpec + specification = cropSpec, + maybeFullDimensions = Some(dimensions) ) - markersWithCropDetails = logMarker ++ Map("imageId" -> apiImage.id, "cropId" -> Crop.getCropId(cropSpec.bounds)) + markersWithCropDetails = logMarker ++ Map("imageId" -> apiImage.id, "cropId" -> crop.id) ExportResult(id, masterSizing, sizings) <- crops.export(apiImage, crop)(markersWithCropDetails) finalCrop = Crop.createFromCrop(crop, masterSizing, sizings) } yield (id, finalCrop) diff --git a/cropper/app/lib/Crops.scala b/cropper/app/lib/Crops.scala index d79dcb4a94..a84e8fb0aa 100644 --- a/cropper/app/lib/Crops.scala +++ b/cropper/app/lib/Crops.scala @@ -97,7 +97,7 @@ class Crops(config: CropperConfig, store: CropStore, imageOperations: ImageOpera val hasAlpha = apiImage.fileMetadata.colourModelInformation.get("hasAlpha").flatMap(a => Try(a.toBoolean).toOption).getOrElse(true) val cropType = Crops.cropType(mimeType, colourType, hasAlpha) - Stopwatch(s"making crop assets for ${apiImage.id} ${Crop.getCropId(source.bounds)}") { + Stopwatch(s"making crop assets for ${apiImage.id} ${crop.id}") { for { sourceFile <- tempFileFromURL(secureUrl, "cropSource", "", config.tempDir) colourModel <- ImageOperations.identifyColourModel(sourceFile, mimeType) diff --git a/cropper/app/model/ExportRequest.scala b/cropper/app/model/ExportRequest.scala index f99bc6c019..0f85ef2d1d 100644 --- a/cropper/app/model/ExportRequest.scala +++ b/cropper/app/model/ExportRequest.scala @@ -16,6 +16,8 @@ case class FullExportRequest(uri: String) extends ExportRequest case class CropRequest(uri: String, bounds: Bounds, aspectRatio: Option[String]) extends ExportRequest +case class PointsOfInterest(uri: String, bounds: Bounds) extends ExportRequest + object ExportRequest { @@ -27,6 +29,11 @@ object ExportRequest { (__ \ "aspectRatio").readNullable[String](pattern(aspectRatioLike)) )(CropRequest.apply _) + private val readPointsOfInterestRequest: Reads[PointsOfInterest] = ( + (__ \ "source").read[String] ~ + __.read[Bounds] + )(PointsOfInterest.apply _) + private val readFullExportRequest: Reads[FullExportRequest] = (__ \ "source").read[String].map(FullExportRequest.apply) @@ -41,6 +48,8 @@ object ExportRequest { def boundsFill(dimensions: Dimensions): Bounds = Bounds(0, 0, dimensions.width, dimensions.height) def toCropSpec(cropRequest: ExportRequest, dimensions: Dimensions): CropSpec = cropRequest match { + case PointsOfInterest(uri, bounds) => + CropSpec(uri, bounds, None, PointsOfInterestExport) case FullExportRequest(uri) => CropSpec( uri, diff --git a/kahuna/public/js/crop/controller.js b/kahuna/public/js/crop/controller.js index 8c939342e6..803615925c 100644 --- a/kahuna/public/js/crop/controller.js +++ b/kahuna/public/js/crop/controller.js @@ -3,7 +3,7 @@ import angular from 'angular'; import '../components/gr-keyboard-shortcut/gr-keyboard-shortcut'; import {radioList} from '../components/gr-radio-list/gr-radio-list'; import {cropUtil} from "../util/crop"; -import {cropOptions} from "../util/constants/cropOptions"; +import {cropOptions, pointsOfInterestBeta} from "../util/constants/cropOptions"; import {getFeatureSwitchActive} from "../components/gr-feature-switch-panel/gr-feature-switch-panel"; const crop = angular.module('kahuna.crop.controller', [ @@ -187,6 +187,8 @@ crop.controller('ImageCropCtrl', [ && getFeatureSwitchActive("show-cropping-gutters-switch") && maybeCropRatioIfStandard === "5:3"; + ctrl.isPointsOfInterestCrop = newCropType === pointsOfInterestBeta.key; /* TODO adjust the height of easel to avoid scrollbar from explainer */ + if (isCropTypeDisabled) { ctrl.cropType = oldCropType; } else { diff --git a/kahuna/public/js/crop/view.html b/kahuna/public/js/crop/view.html index 5a98a5d46f..920533337a 100644 --- a/kahuna/public/js/crop/view.html +++ b/kahuna/public/js/crop/view.html @@ -106,6 +106,12 @@ there is nothing important in the striped sides as these might be clipped. + +