quarta-feira, 28 de agosto de 2019

Widget de Upload de Fotos

Este é um widget para uso com aplicativos do Web AppBuilder, da ESRI. Ele usa ArcGIS API for Javascript e permite realizar o upload de fotos por um serviço REST em um servidor ArcGIS.

O widget usa a estrutura padrão do WAB, com uma pasta com seu nome constando dentro da pasta widgets do app e com subpastas e arquivos na seguinte estrutura:


O widget usa a biblioteca EXIF.js que foi colocada na mesma pasta do widget, como pode ser verificado na imagem acima.

Abaixo segue o código de Widget.js, Widget.html e style.css:

// AppGeo - 2019

define([
'dojo/_base/declare',
'dojo/_base/lang',
'dojo/aspect',
'dojo/Deferred',
"dojo/dom",
"dojo/json",
"dojo/on",
"dojo/parser",
"dojo/sniff",
"dojo/_base/array",
"dijit/form/Button",
'dijit/_WidgetsInTemplateMixin',
'jimu/BaseWidget',
'jimu/portalUtils',
'jimu/dijit/Message',
'jimu/loaderplugins/jquery-loader!https://code.jquery.com/jquery-git1.min.js',
"esri/config",
"esri/request",
"esri/layers/FeatureLayer",
'esri/tasks/JobInfo',
'esri/tasks/query',
'esri/tasks/QueryTask',
'widgets/AddPhotoGeo/exif'
],
function (
declare,
lang,
aspect,
Deferred,
dom,
JSON,
on,
parser,
sniff,
arrayUtils,
Button,
_WidgetsInTemplateMixin,
BaseWidget,
PortalUtils,
Message,
$,
esriConfig,
request,
FeatureLayer,
JobInfo,
Query,
QueryTask,
exif
) {

// url raiz consta no config, foi declarada como global devido a limitacoes para buscar o config dentro do EXIF
// url para adicionar o ponto
var urlAF = "https://mapas.teste.com/server/rest/services/APPGEO/FOTOS_GEORREFERENCIADAS/FeatureServer/0/addFeatures";

// url basica usada na adicao de anexo
var url = "https://mapas.teste.com/server/rest/services/APPGEO/FOTOS_GEORREFERENCIADAS/FeatureServer/0/";

// object id da feicao que recebera anexo
var oid = null;

// variaveis dos atributos de texto
var idProcesso = null;
var idProcDet = null;
var obs = null;
var orientacao = null;
var proprietario = null;
var idEmpreendimento = null;
var clazz = declare([BaseWidget, _WidgetsInTemplateMixin], {

// funcao de carregamento do widget
startup: function () {

// evento de mudanca nos elementos onde o usuario insere o texto, dispara processamento
on(dom.byId("textForm"), "change", lang.hitch(this, function (event) {

// armazena campos de texto

// Se idProcesso/idEmpreendimento/idProcDet nao forem numeros, armazena null ao inves de ""
if (document.getElementById("idP").value != ""){
var idP = parseInt(document.getElementById("idP").value);
if (isNaN(idP)) {
idProcesso = null;
} else {
idProcesso = idP;
}
} else {
idProcesso = null;
}

if (document.getElementById("idE").value != "") {
var idE = parseInt(document.getElementById("idE").value);
if (isNaN(idE)) {
idEmpreendimento = null;
} else {
idEmpreendimento = idE;
}
} else {
idEmpreendimento = null;
}
if (document.getElementById("idPD").value != ""){
var idPD = parseInt(document.getElementById("idPD").value);
if (isNaN(idPD)) {
idProcDet = null;
} else {
idProcDet = idPD;
}
} else {
idProcDet = null;
}
// Se obs/orientacao/proprietario estiverem vazios, armazena null ao inves de ""
if (document.getElementById("obs").value != "") {
obs = String(document.getElementById("obs").value);
} else {
obs = null;
}
if (document.getElementById("orient").value != ""){
orientacao = String(document.getElementById("orient").value);
} else {
orientacao = null;
}
if (document.getElementById("prop").value != ""){
proprietario = String(document.getElementById("prop").value);
} else {
proprietario = null;
}

dom.byId('upload-status').innerHTML = '<p style="color:orange">Ao terminar de preencher os campos, clique em "Adicionar" para inserir a foto e enviar os dados.</p>';
}));


// evento de mudanca no elemento onde o usuario insere o arquivo, dispara processamento
on(dom.byId("uploadForm"), "change", lang.hitch(this, function (event) {

// armazena caminho do arquivo (modificado pelo navegador)
var fileName = event.target.value.toLowerCase();

//separa nome do arquivo
var name = fileName.split(".");
name = name[0].replace("c:\\fakepath\\", "");

//armazena arquivo capturado pelo evento em uma variavel
var f = event.target.files[0];

if (sniff("ie")) { //filename is full path in IE so extract the file name
var arr = fileName.split("\\");
fileName = arr[arr.length - 1];
}
if (fileName.indexOf(".jpg") !== -1) {//is file a jpg - if not notify user
this.getPhotoMetadata(f);//chama funcao que le atributos da foto e envia para o servidor
}
else {
dom.byId('upload-status').innerHTML = '<p style="color:red">Adicionar foto no formato (.jpg)</p>';
}
}));
},


// funcao que le atributos da foto, cria ponto e anexa foto
getPhotoMetadata: lang.hitch(this, function (img) {
// se arquivo nao for de imagem, sai da funcao e avisa o usuario
if (img == null) {
dom.byId('upload-status').innerHTML = '<p style="color:red">Adicionar um arquivo de foto</p>';
return;
}
// usa biblioteca para ler atributos
EXIF.getData(img, function () {

var metadata = EXIF.getAllTags(this);

// armazena data da foto
var fulldate = metadata.DateTimeOriginal || metadata.DateTimeDigitized || metadata.DateTime || null;
// formata data
if (fulldate != null) {
var date = fulldate.split(' ')[0];
var parts = date.split(':');
date = parts[0] + '-' + parts[1] + '-' + parts[2]

fulldate = date + ' ' + fulldate.split(' ')[1];
}

//validacao do geotag - se nao houver geotag, sai da funcao e avisa o usuario
var x_verif = metadata.GPSLongitude || "Sem Informacao de Longitude";
var y_verif = metadata.GPSLatitude || "Sem Informacao de Latitude";

if (x_verif == "Sem Informacao de Longitude" || y_verif == "Sem Informacao de Latitude") {
dom.byId('upload-status').innerHTML = '<p style="color:red">Adicionar foto com geotag preenchido</p>';
return;
}

// funcao para converter o objeto com coordenadas em gms para decimais
var toDecimal = function (number) {
return number[0].numerator + number[1].numerator /
(60 * number[1].denominator) + number[2].numerator / (3600 * number[2].denominator);
};

// coordenadas x, y (convertidas) e z
var x_decimal = toDecimal(metadata.GPSLongitude);
var y_decimal = toDecimal(metadata.GPSLatitude);
if (metadata.GPSLongitudeRef == "W") {
x_decimal = x_decimal * -1
}
if (metadata.GPSLatitudeRef == "S") {
y_decimal = y_decimal * -1
}
var z = metadata.GPSAltitude || null;

// Add feature e realizado dentro do EXIF devido a limitacoes para retornar outro valor
//alem de true/false ou mesmo utilizar uma variavel global fora desta funcao (o valor global
//continua o mesmo se for acionado no trecho de uploadForm change antes/apos a ativacao desta procedure)

// geometria e campos da feicao que sera adicionada
var feature = {
"geometry": { "x": x_decimal, "y": y_decimal },
"attributes": {
"idProcesso": idProcesso,
"idProcDet": idProcDet,
"obs": obs,
"dataCarga": Date.now(), //GMT 0
"latitude": y_decimal,
"longitude": x_decimal,
"altitude": z.valueOf(),
"dataFoto": fulldate,
"orientacao": orientacao,
"proprietario": proprietario,
"importadoDe": "Appgeo - widget de fotos",
"linkFoto": null,
"dataAtualizaSARE": null,
"dataAtualizaDen": null,
"dataAtualizaAP": null,
"dataAtualizaN": null,
"idEmpreendimento": idEmpreendimento
}
};

// post que envia feicao
$.post(urlAF, {
features: JSON.stringify([feature]),
f: "json"
})
.done(lang.hitch(this, function (results) {//post bem-sucedido
console.log(results);

// timeout para aguardar results e verificar se ha resultado de erro que entra em done ao inves de fail
setTimeout(function () {
var cod = results.error.code;
console.log(cod);
if (cod == 499) {
dom.byId('upload-status').innerHTML = '<p style="color:red">Voce nao tem permissao para utilizar este widget.</p>'
} else {
dom.byId('upload-status').innerHTML = '<p style="color:green">Ponto criado. Adicionando anexo. Aguarde a resposta do servidor.</p>'
}
}, 100);

//dom.byId('upload-status').innerHTML = '<p style="color:green">Ponto criado. Adicionando anexo. Aguarde a resposta do servidor.</p>';

// timeout para aguardar results preenchidos e entao anexar arquivo
setTimeout(function () {
// converte json de resposta em objeto js
var resultAF = JSON.parse(results);

// converte em integer o objectId
oid = parseInt(resultAF.addResults[0].objectId);

// Trecho que adiciona anexo se insercao do ponto foi bem-sucedida
var featureLayer = new esri.layers.FeatureLayer(url, { outFields: ["*"], visible: false });
featureLayer.addAttachment(oid, document.getElementById("uploadForm"), callback, function (err) {
console.log(err);//addAttachment falhou
dom.byId('upload-status').innerHTML = '<p style="color:red">Erro ao anexar foto. Informe a equipe do AppGeo.</p>';
});
function callback(result) {//addAttachment bem-sucedido
console.log(result);
dom.byId('upload-status').innerHTML = '<p style="color:green">Tarefa concluida: ponto criado e foto anexada.</p>';
};
}, 100);
}))
.fail(function (error) {//post falhou
console.log(error);
dom.byId('upload-status').innerHTML = '<p style="color:red">Erro na criacao do ponto. Tente novamente ou informe equipe do AppGeo.</p>';
});
});
}),

destroy: function () {
this.inherited(arguments);
}
});
return clazz;
});


<div>
<p>
<b>Preencha os dados relevantes da foto</b>
</p><p>
</p><br /><br />
<form enctype="multipart/form-data" method="post" id="textForm">
<div class="tooltip">
NIS do processo: <input type="text" name="idProcesso" id="idP" maxlength="10"/><br /><br />
<span class="tooltiptext">ID do processo, se houver</span>
</div>
<div class="tooltip">
NIS do empreendimento: <input type="text" name="idEmpreendimento" id="idE" maxlength="10" /><br /><br />
<span class="tooltiptext">ID do empreendimento, se houver</span>
</div>
<div class="tooltip">
idProcDet: <input type="text" name="idProcDet" id="idPD" maxlength="10"/><br /><br />
<span class="tooltiptext">Id da Atividade, se houver</span>
</div>
<div class="tooltip">
obs: <input type="text" name="obs" id="obs" maxlength="300"/><br /><br />
<span class="tooltiptext">Observacoes</span>
</div>
<div class="tooltip">
visada: <input type="text" name="orientacao" id="orient" maxlength="20"/><br /><br />
<span class="tooltiptext">Direcao de visada da foto</span>
</div>
<div class="tooltip">
proprietario: <input type="text" name="proprietario" id="prop" maxlength="150"/><br /><br />
<span class="tooltiptext">Nome do proprietario da foto</span>
</div>
</form><br /><br />
<p>
<b>Adicione uma foto com geotag e formato jpg</b>
</p><p>
</p>
<form enctype="multipart/form-data" method="post" id="uploadForm">
<div class="field">
<label class="file-upload">
<span><strong>Adicionar Arquivo</strong></span>
<input type="file" name="attachment" id="inFile" />
</label>
</div>
</form>
<span class="file-upload-status" style="opacity:1;" id="upload-status"></span>
<div id="fileInfo"> </div>
</div>

/*Photo upload form styling */
.field {
padding:4px 4px;
}
.file-upload {
overflow: hidden;
display: inline-block;
position: relative
vertical-align: middle;
text-align: center;
border: 2px solid #666666;
background: #444444;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
border-radius: 6px;
color: #fff;
text-shadow: 0px 2px 3px #555;
cursor:pointer;
}
  
.file-upload:hover {
background-color: #454545;
}

.file-upload input {
position: absolute;
top: 0;
left: 0;
margin: 0;
font-size: 70px;
opacity: 0;
filter: alpha(opacity=0);
z-index:2;
}
.file-upload strong {
font: normal 1.75em arial,sans-serif;
  
.file-upload span {
position: absolute;
top: 0;
left: 0;
display: inline-block;

padding-top: .45em;
}

.file-upload { height: 3em; }
.file-upload,
.file-upload spanwidth: 10em; }  

/*Text form styling*/

/* Tooltip container */
.tooltip {
position: relative;
display: inline-block;
}

/* Tooltip text */
.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
background-color: #454545;
color: #fff;
text-align: center;
padding: 5px 0;
border-radius: 6px;

/* Position the tooltip text */
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -60px;

/* Fade in tooltip */
opacity: 0;
transition: opacity 0.3s;
}

/* Show the tooltip text when you mouse over the tooltip container */
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}

.tooltip input {
width: 150px;
}

Nenhum comentário:

Postar um comentário