Skip to content

Commit

Permalink
WIP add points of interest ᴮᴱᵀᴬ crop type
Browse files Browse the repository at this point in the history
  • Loading branch information
twrichards committed Nov 1, 2024
1 parent 427322f commit 2dbdc1c
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 9 deletions.
25 changes: 21 additions & 4 deletions common-lib/src/main/scala/com/gu/mediaservice/model/Crop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {

Expand All @@ -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))
Expand Down
5 changes: 3 additions & 2 deletions cropper/app/controllers/CropperController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion cropper/app/lib/Crops.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions cropper/app/model/ExportRequest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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)

Expand All @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion kahuna/public/js/crop/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', [
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions kahuna/public/js/crop/view.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@
there is nothing important in the striped sides as these might be clipped.
</div>


<div class="warning status--info" ng-if="ctrl.isPointsOfInterestCrop">
This is a 'Points of Interest ᴮᴱᵀᴬ` crop. Use the crop rectangle to cover all the vital points of interest, but nothing more.
The frontend can use the crop in spaces of different shapes/ratios ensuring the important parts are always visible.
</div>

<div class="easel" role="main" aria-label="Image cropper"
ng-class="{'vertical-warning-gutters': ctrl.shouldShowVerticalWarningGutters}">
<div class="easel__canvas">
Expand Down
3 changes: 2 additions & 1 deletion kahuna/public/js/util/constants/cropOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export const portrait = {key: 'portrait', ratio: 4 / 5, ratioString: '4:5'};
export const video = {key: 'video', ratio: 16 / 9, ratioString: '16:9'};
export const square = {key: 'square', ratio: 1, ratioString: '1:1'};
export const freeform = {key: 'freeform', ratio: null};
export const pointsOfInterestBeta = {key: 'points of interest ᴮᴱᵀᴬ', ratio:null};

export const cropOptions = [landscape, portrait, video, square, freeform];
export const cropOptions = [landscape, portrait, video, square, freeform, pointsOfInterestBeta];

0 comments on commit 2dbdc1c

Please sign in to comment.