map image updates

pull/659/head
gabrielburnworth 2018-02-12 23:20:38 -08:00
parent 332d7748b7
commit 7300150ff4
10 changed files with 74 additions and 25 deletions

View File

@ -43,7 +43,8 @@ describe("<FarmDesigner/>", () => {
cameraCalibrationData: {
scale: undefined, rotation: undefined,
offset: { x: undefined, y: undefined },
origin: undefined
origin: undefined,
calibrationZ: undefined
},
};
}

View File

@ -243,4 +243,5 @@ export interface CameraCalibrationData {
y: string | undefined;
},
origin: string | undefined;
calibrationZ: string | undefined;
}

View File

@ -82,7 +82,8 @@ function fakeProps(): GardenMapProps {
cameraCalibrationData: {
scale: undefined, rotation: undefined,
offset: { x: undefined, y: undefined },
origin: undefined
origin: undefined,
calibrationZ: undefined
},
};
}

View File

@ -26,7 +26,8 @@ describe("<MapImage />", () => {
offset: { x: undefined, y: undefined },
origin: undefined,
rotation: undefined,
scale: undefined
scale: undefined,
calibrationZ: undefined
},
mapTransformProps: {
gridSize: { x: 0, y: 0 },
@ -40,13 +41,6 @@ describe("<MapImage />", () => {
expect(wrapper.html()).toEqual("<image></image>");
});
it("doesn't render placeholder image", () => {
const p = fakeProps();
p.image && (p.image.body.attachment_url = "/placehold.");
const wrapper = mount(<MapImage {...p} />);
expect(wrapper.html()).toEqual("<image></image>");
});
interface ExpectedData {
size: { width: number, height: number };
sx: number;
@ -74,11 +68,14 @@ describe("<MapImage />", () => {
const INPUT_SET_1 = fakeProps();
INPUT_SET_1.image && (INPUT_SET_1.image.body.meta.x = 0);
INPUT_SET_1.image && (INPUT_SET_1.image.body.meta.y = 0);
INPUT_SET_1.image && (INPUT_SET_1.image.body.meta.z = 0);
INPUT_SET_1.image && (INPUT_SET_1.image.body.meta.name = "rotated_image");
INPUT_SET_1.cameraCalibrationData = {
offset: { x: "50", y: "75" },
origin: "\"TOP_RIGHT\"",
rotation: "-57.45",
scale: "0.8041"
scale: "0.8041",
calibrationZ: "0"
};
INPUT_SET_1.mapTransformProps = {
gridSize: { x: 5900, y: 2900 },
@ -87,12 +84,15 @@ describe("<MapImage />", () => {
INPUT_SET_1.sizeOverride = { width: 480, height: 640 };
const INPUT_SET_2 = cloneDeep(INPUT_SET_1);
INPUT_SET_2.image && (INPUT_SET_2.image.body.meta = { x: 221, y: 308, z: 1 });
INPUT_SET_2.image && (INPUT_SET_2.image.body.meta = {
x: 221, y: 308, z: 1, name: "marked_image"
});
INPUT_SET_2.cameraCalibrationData.origin = "TOP_RIGHT";
INPUT_SET_2.mapTransformProps.quadrant = 1;
const INPUT_SET_3 = cloneDeep(INPUT_SET_2);
INPUT_SET_3.mapTransformProps.quadrant = 2;
INPUT_SET_3.image && (INPUT_SET_3.image.body.meta.name = "calibration_result");
const INPUT_SET_4 = cloneDeep(INPUT_SET_3);
INPUT_SET_4.mapTransformProps.quadrant = 3;
@ -136,4 +136,24 @@ describe("<MapImage />", () => {
size: expectedSize, sx: 1, sy: 1, tx: 5436.016, ty: 2259.688
});
it("doesn't render placeholder image", () => {
const p = INPUT_SET_1;
p.image && (p.image.body.attachment_url = "/placehold.");
const wrapper = mount(<MapImage {...p} />);
expect(wrapper.html()).toEqual("<image></image>");
});
it("doesn't render image taken at different height than calibration", () => {
const p = INPUT_SET_1;
p.image && (p.image.body.meta.z = 100);
const wrapper = mount(<MapImage {...p} />);
expect(wrapper.html()).toEqual("<image></image>");
});
it("doesn't render images that are not adjusted for camera rotation", () => {
const p = INPUT_SET_1;
p.image && (p.image.body.meta.name = "na");
const wrapper = mount(<MapImage {...p} />);
expect(wrapper.html()).toEqual("<image></image>");
});
});

View File

@ -5,9 +5,12 @@ import { fakeImage } from "../../../../__test_support__/fake_state/resources";
describe("<ImageLayer/>", () => {
function fakeProps(): ImageLayerProps {
const image = fakeImage();
image.body.meta.z = 0;
image.body.meta.name = "rotated_image";
return {
visible: true,
images: [fakeImage()],
images: [image],
mapTransformProps: {
quadrant: 2, gridSize: { x: 3000, y: 1500 }
},
@ -15,7 +18,8 @@ describe("<ImageLayer/>", () => {
offset: { x: "0", y: "0" },
origin: "TOP_LEFT",
rotation: "0",
scale: "1"
scale: "1",
calibrationZ: "0"
},
sizeOverride: { width: 10, height: 10 }
};

View File

@ -3,7 +3,7 @@ import { MapTransformProps } from "../interfaces";
import { CameraCalibrationData } from "../../interfaces";
import { TaggedImage } from "../../../resources/tagged_resources";
import { MapImage } from "../map_image";
import { reverse } from "lodash";
import { reverse, cloneDeep } from "lodash";
export interface ImageLayerProps {
visible: boolean;
@ -19,7 +19,7 @@ export function ImageLayer(props: ImageLayerProps) {
} = props;
return <g id="image-layer">
{visible &&
reverse(images).map(img =>
reverse(cloneDeep(images)).map(img =>
<MapImage
image={img}
key={"image_" + img.body.id}

View File

@ -13,6 +13,22 @@ const parse = (str: string | undefined) => {
return !isNaN(parsed) ? parsed : undefined;
};
/* Check if the image has been rotated according to the calibration value. */
const isRotated = (name: string | undefined) => {
return name &&
(name.includes("rotated")
|| name.includes("marked")
|| name.includes("calibration_result"));
};
/* Check if the calibration data is valid for the image provided using z. */
const cameraZCheck =
(imageZ: number | undefined, calibZ: string | undefined) => {
const calibrationZ = parse(calibZ);
return isNumber(imageZ) && isNumber(calibrationZ) &&
Math.abs(imageZ - calibrationZ) < 5;
};
interface ImageSize {
width: number;
height: number;
@ -20,8 +36,8 @@ interface ImageSize {
/* Get the size of the image at the URL. Allow overriding for tests. */
const getImageSize = (url: string, size?: ImageSize): ImageSize => {
if (size) { return size; }
if (url.includes("placehold")) { return { width: 0, height: 0 }; }
if (size) { return size; }
const imageData = new Image();
imageData.src = url;
return {
@ -102,9 +118,8 @@ export interface MapImageProps {
*/
export function MapImage(props: MapImageProps) {
const { image, cameraCalibrationData, sizeOverride } = props;
const { scale, rotation, offset, origin } = cameraCalibrationData;
const { scale, offset, origin, calibrationZ } = cameraCalibrationData;
const imageScale = parse(scale);
const imageRotation = parse(rotation);
const imageOffsetX = parse(offset.x);
const imageOffsetY = parse(offset.y);
const imageOrigin = origin ? origin.split("\"").join("") : undefined;
@ -113,12 +128,14 @@ export function MapImage(props: MapImageProps) {
/* Check if the image exists. */
if (image) {
const imageUrl = image.body.attachment_url;
const { x, y } = image.body.meta;
const { x, y, z } = image.body.meta;
const imageAnnotation = image.body.meta.name;
const { width, height } = getImageSize(imageUrl, sizeOverride);
/* Check for all necessary camera calibration and image data. */
if (isNumber(x) && isNumber(y) && height > 0 && width > 0 &&
isNumber(imageScale) && isNumber(imageRotation) &&
isNumber(imageScale) && imageScale > 0 &&
cameraZCheck(z, calibrationZ) && isRotated(imageAnnotation) &&
isNumber(imageOffsetX) && isNumber(imageOffsetY) && imageOrigin) {
/* Use pixel to coordinate scale to scale image. */
const size = { x: width * imageScale, y: height * imageScale };

View File

@ -55,7 +55,7 @@ export function mapStateToProps(props: Everything) {
const latestImages = _(selectAllImages(props.resources.index))
.sortBy(x => x.body.id)
.reverse()
.take(5)
.take(50)
.value();
const { user_env } = props.bot.hardware;
@ -67,6 +67,7 @@ export function mapStateToProps(props: Everything) {
y: user_env["CAMERA_CALIBRATION_camera_offset_y"]
},
origin: user_env["CAMERA_CALIBRATION_image_bot_origin_location"],
calibrationZ: user_env["CAMERA_CALIBRATION_camera_z"],
};
return {

View File

@ -11,6 +11,7 @@ export interface Image {
x: number | undefined;
y: number | undefined;
z: number | undefined;
name?: string;
};
}

View File

@ -43,9 +43,12 @@ export class Photos extends React.Component<PhotosProps, {}> {
const i = this.props.currentImage;
if (i) {
const { meta } = i.body;
return Object.keys(meta).sort().map(function (key, index) {
return <MetaInfo key={index} attr={key} obj={meta} />;
});
return Object.keys(meta)
.filter(key => ["x", "y", "z"].includes(key))
.sort()
.map((key, index) => {
return <MetaInfo key={index} attr={key} obj={meta} />;
});
} else {
return <MetaInfo attr={t("image")} obj={{ image: t("No meta data.") }} />;
}