Set EXIF geolocation using CameraX - exif

How do you add Location information in the image EXIF using CameraX API. I created my own app using https://codelabs.developers.google.com/codelabs/camerax-getting-started/#4.
I sifted through https://developer.android.com/training/camerax and https://developer.android.com/jetpack/androidx/releases/exifinterface but did not found any guides.

If you took a picture you override onImageSaved. There you can write the exif data to your photo. I only have it in Java, but the princip is the same.
ExifInterface exif = null;
try {
exif = new ExifInterface(PhotoPath);
} catch (IOException e) {
e.printStackTrace();
}
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, "location");
The different TAGs you can look up here. How to get the location is here and if you need more information about how to store the location in exif look here.

You can set the location with the Metadata.setLocation method
val metadata = Metadata().apply {
location = Location() // set the location here
}
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, System.currentTimeMillis())
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
}
val contentUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val outputOptions = OutputFileOptions.Builder(contentResolver, contentUri, contentValues)
.setMetadata(metadata)
.build()
imageCapture.takePicture(outputOptions, mainExecutor, object : OnImageSavedCallback {
override fun onImageSaved(outputFileResults: OutputFileResults) {
Log.d(TAG, "Photo URI ${outputFileResults.savedUri}")
}
override fun onError(exception: ImageCaptureException) {
Log.e(TAG, "Error", exception)
}
})

Related

What is the new button name for Base.Actions["LSPOReceiptLine_binLotSerial"].Press()?

I have inherited an older customization to the Purchase Receipts / PO302000 screen that I'm trying to upgrade, and it had customization code to import Lot/Serial nbrs from an Excel spreadsheet. It all seems to work alright, except that at the end, it errors out when pressing a button as follows:
Base.Actions["LSPOReceiptLine_binLotSerial"].Press();
Here's the entire code:
public virtual void importAllocations()
{
try
{
if (Base.transactions.Current != null)
{
var siteid = Base.transactions.Current.SiteID;
if (Base.splits.Select().Count == 0)
{
if (this.NewRevisionPanel.AskExt() == WebDialogResult.OK)
{
const string PanelSessionKey = "ImportStatementProtoFile";
PX.SM.FileInfo info = PX.Common.PXContext.SessionTyped<PXSessionStatePXData>().FileInfo[PanelSessionKey] as PX.SM.FileInfo;
System.Web.HttpContext.Current.Session.Remove(PanelSessionKey);
if (info != null)
{
byte[] filedata = info.BinData;
using (NVExcelReader reader = new NVExcelReader())
{
Dictionary<UInt32, string[]> data = reader.loadWorksheet(filedata);
foreach (string[] textArray in data.Values)
{
if (textArray[0] != GetInventoryCD(Base.transactions.Current.InventoryID))
{
throw (new Exception("InventoryID in file does not match row Inventory ID"));
}
else
{
//Find the location ID based on the location CD provided by the Excel sheet...
INLocation inloc = PXSelect<INLocation,
Where<INLocation.locationCD, Equal<Required<INLocation.locationCD>>,
And<INLocation.siteID, Equal<Required<INLocation.siteID>>>>>.Select(Base
, textArray[1]
, Base.transactions.Current.SiteID);
Base.splits.Insert(new POReceiptLineSplit()
{
InventoryID = Base.transactions.Current.InventoryID,
LocationID = inloc.LocationID, //Convert.ToInt32(textArray[1]), //Base.transactions.Current.LocationID,
LotSerialNbr = textArray[2],
Qty = Decimal.Parse(textArray[3])
});
}
}
}
}
}
}
}
Base.Actions["LSPOReceiptLine_binLotSerial"].Press();
}
catch (FileFormatException fileFormat)
{
// Acuminator disable once PX1053 ConcatenationPriorLocalization [Justification]
throw new PXException(String.Format("Incorrect file format. File must be of type .xlsx", fileFormat.Message));
}
catch (Exception ex)
{
throw ex;
}
}
Now, there seems to be no such button - and I have no idea what it would be called now, or if it even still exists. I don't even really know what this action did.
Any ideas?
Thanks much...
That logic has been moved into the PX.Objects.PO.GraphExtensions.POReceiptEntryExt.POReceiptLineSplittingExtension. That action was doing the following in the PX.Objects.PO.LSPOReceiptLine
// PX.Objects.PO.LSPOReceiptLine
// Token: 0x0600446F RID: 17519 RVA: 0x000EE86C File Offset: 0x000ECA6C
public override IEnumerable BinLotSerial(PXAdapter adapter)
{
if (base.MasterCache.Current != null)
{
if (!this.IsLSEntryEnabled((POReceiptLine)base.MasterCache.Current))
{
throw new PXSetPropertyException("The Line Details dialog box cannot be opened because changing line details is not allowed for the selected item.");
}
this.View.AskExt(true);
}
return adapter.Get();
}
Now it is called ShowSplits and is part of the POReceiptLineSplittingExtension extension.
// PX.Objects.PO.GraphExtensions.POReceiptEntryExt.POReceiptLineSplittingExtension
// Token: 0x06005359 RID: 21337 RVA: 0x00138621 File Offset: 0x00136821
public override IEnumerable ShowSplits(PXAdapter adapter)
{
if (base.LineCurrent == null)
{
return adapter.Get();
}
if (!this.IsLSEntryEnabled(base.LineCurrent))
{
throw new PXSetPropertyException("The Line Details dialog box cannot be opened because changing line details is not allowed for the selected item.");
}
return base.ShowSplits(adapter);
}
Given the fact that ShowSplits is defined in the LineSplittingExtension originally it may be referred to as "LineSplittingExteions_ShowSplits" or "POReceiptLineSplittingExtension_ShowSplits". I would suggest including that POReceiptLineSplittingExtension as part of your extension and simply call the Base1.ShowSplits

Amazon DynamoDB :- Invalid UpdateExpression: Expression size has exceeded the maximum allowed size dynamodb

I am trying to update an item in was dynamoDB using nodes, db.updateItem(query).
I am getting the following error :
Invalid UpdateExpression: Expression size has exceeded the maximum allowed size dynamodb
On reading few posts, I realised that dynamoDB allows itemSize to be 400KB and that might be a problem here. But if that is the problem, why did it allow to insert the item in the first place.
I am not sure what exactly the issue. Any help would be appreciated.
Please let me know if I missed any required information
You are probably hitting Expression Parameters limits. Please refer to:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html#limits-expression-parameters
If you are getting this exception
software.amazon.awssdk.services.dynamodb.model.DynamoDbException: Item size has exceeded the maximum allowed size
This exception is due to AWS Dynamodb limits mentioned here
in my case, I compressed the record using gzip and stored binary zipped data, and uncompressed it back after reading that record.
please see below sample code to compress and decompress (I am using enhanced dynamodb client library)
public CompletableFuture<Boolean> storeItem(MyBeanClass object) {
CompletableFuture<Boolean> completableFuture = CompletableFuture.supplyAsync(() -> false);
try {
byte[] serialized = objectMapper.writeValueAsString(object.getLargeData()).getBytes(UTF_8);
if (serialized.length >= 10000) { //large record, gzip it
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(serialized.length);
GZIPOutputStream gzip = new GZIPOutputStream(bos)) {
gzip.write(serialized);
gzip.close();
MyBeanClass newObject = new MyBeanClass();
newObject.setPrimaryId(object.getPrimaryId());
newObject.setGzData(SdkBytes.fromByteArray(bos.toByteArray()));
completableFuture = enhancedDynamoDbTable.putItem(newObject)
.thenApply(res -> true)
.exceptionally(th -> {
th.printStackTrace();
return false;
});
}
} else { //no compression required
completableFuture = enhancedDynamoDbTable.putItem(object).thenApply(res -> true)
.exceptionally(th -> {
th.printStackTrace();
return false;
});
}
} catch (IOException e) {
e.printStackTrace();
}
To fetch record and unzip
public CompletableFuture<MyBeanClass> getItem(String id) {
return enhancedDynamoDbTable
.getItem(Key.builder().partitionValue(id).build())
.thenApply(record -> {
if (record.getGzData() != null) {
try (ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(record.getGzData().asByteArray());
GZIPInputStream inputStream = new GZIPInputStream(arrayInputStream);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, length);
}
record = objectMapper.readValue(byteArrayOutputStream.toString(UTF_8), MyBeanClass.class);
} catch (IOException e) {
e.printStackTrace();
}
}
return record;
});
}
Hope that helps.

Testing for file upload in Spring MVC

Project setup:
<java.version>1.8</java.version>
<spring.version>4.3.9.RELEASE</spring.version>
<spring.boot.version>1.4.3.RELEASE</spring.boot.version>
We have a REST controller that has a method to upload file like this:
#PostMapping("/spreadsheet/upload")
public ResponseEntity<?> uploadSpreadsheet(#RequestBody MultipartFile file) {
if (null == file || file.isEmpty()) {
return new ResponseEntity<>("please select a file!", HttpStatus.NO_CONTENT);
} else if (blueCostService.isDuplicateSpreadsheetUploaded(file.getOriginalFilename())) {
return new ResponseEntity<>("Duplicate Spreadsheet. Please select a different file to upload",
HttpStatus.CONFLICT);
} else {
try {
saveUploadedFiles(Arrays.asList(file));
} catch (IOException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity("Successfully uploaded - " + file.getOriginalFilename(), new HttpHeaders(),
HttpStatus.OK);
}
}
UPDATE:
I've tried this approach from an old example I found, but it doesn't compile cleanly, the MockMvcRequestBuilders.multipart method is not defined....
#Test
public void testUploadSpreadsheet_Empty() throws Exception {
String fileName = "EmptySpreadsheet.xls";
String content = "";
MockMultipartFile mockMultipartFile = new MockMultipartFile(
"emptyFile",
fileName,
"text/plain",
content.getBytes());
System.out.println("emptyFile content is '" + mockMultipartFile.toString() + "'.");
mockMvc.perform(MockMvcRequestBuilders.multipart("/bluecost/spreadsheet/upload")
.file("file", mockMultipartFile.getBytes())
.characterEncoding("UTF-8"))
.andExpect(status().isOk());
}
I believe MockMvcRequestBuilders.multipart() is only available since Spring 5. What you want is MockMvcRequestBuilders.fileUpload() that is available in Spring 4.

How to read Html files by using XmlReader

I want to read HTML file by using XMLReader. So I wrote some codes, but it throws only XmlException. So please give me any suggestion about how to read the HTML file (and tags) line by line using C#.
public class HtmlReader
{
public List<HtmlDocument> Read(string path)
{
List<HtmlDocument> html = new List<HtmlDocument>();
HtmlDocument h1 = new HtmlDocument();
using (XmlReader reader = XmlReader.Create(path.ToString()))
{
try
{
while (reader.Read())
{
if (reader.IsStartElement())
{
if (reader.Name == "title" || reader.Name == "body")
{
switch (reader.Name)
{
case "title":
if (reader.Read())
{
h1.Title = reader.Value.Trim();
}
break;
case "body":
if (reader.Read())
{
}
break;
}
}
}
}
}
catch(XmlException)
{
}
}
return html;
}
}
}
class Program
{
static void Main(string[] args)
{
HtmlReader readerObject = new HtmlReader();
List<HtmlDocument> employeeCollection = readerObject.Read("E:/workoutsPrograms/ConsoleApplication4/Table.html");
}
}
I tried this, but I was not able to read the Html tags line by line. Apart from my expectation, it throws only Exception.
I find the answer for above question. Following codes you can use.
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Ignore;
settings.IgnoreWhitespace = true;
XmlRederSettings is enabled set features of XmlReader. In that Html file have DOCTYPe, For that avoid we want use the Dtdprocessing.Ignore.

Marker is not displayed on HERE Map

I want a Marker to be displayed on the Map, but it's not displayed, here's the code I used,
I'm using HERE Map SDK for Android
Image img = new Image();
try {
img.setImageResource(R.drawable.marker);
} catch (IOException e) {
e.printStackTrace();
}
MapMarker mm = new MapMarker();
mm.setIcon(img);
mm.setCoordinate(new GeoCoordinate(21.609512, 39.131269));
After creating the MapMarker, you need to add it also to your map via Map.addMapObject(...)
See my example (where mMap is my instance of Map, and the anchor point is on the botton instead of center):
private void addMarker(GeoCoordinate geoCoordinate)
{
if (mMarker == null) {
Image image = new Image();
try {
image.setImageResource(R.drawable.pin);
} catch (final IOException e) {
e.printStackTrace();
}
mMarker = new MapMarker(geoCoordinate, image);
mMarker.setAnchorPoint(new PointF(image.getWidth()/2, image.getHeight()));
mMap.addMapObject(mMarker);
} else {
mMarker.setCoordinate(geoCoordinate);
}
mMap.setCenter(geoCoordinate, Animation.BOW);
}

Resources