Wednesday, December 30, 2015

Alterando uma extensão do ThingWorx - Faltou falar de autenticação!

Autenticando!

Pessoal, anteriormente (leiam os posts Como Escolher, Hello World, IDE e Runtime) falei sobre o uso da API HeatMaps do Google mas acabei me esquecendo de um ponto importante: a autenticação!

Como mencionei no primeiro post, o uso da API Heatmaps tem uma cota diária e para tal é necessário fazer a autenticação utilizando-se uma Chave de Autenticação criada para liberar a API e controlar o uso.

Para isso é necessário entrar no Console de Desenvolvedores do Google e criar uma credencial que será associada à API e depois utilizada na aplicação. No help do console há algumas dicas de como fazê-lo.

No meu caso eu criei uma Browser Key (ainda não entendi bem a diferença entre Browser/Server keys),  e como o ThingWorx usa widgets, há um arquivo XML chamado metadata que possui algumas configurações, e uma delas é a chamada da API. Tudo o que tive que fazer foi adicionar a autenticação:

<Widget name="googlemap">
<UIResources>
<!-- Studio ONLY -->
<FileResource type="CSS" file="googlemap.ide.css" description="" isDevelopment="true" isRuntime="false" />
<FileResource type="JS" file="googlemap.ide.js" description="" isDevelopment="true" isRuntime="false" />
<!-- Runtime/Squeal ONLY -->
<FileResource type="CSS" file="googlemap.runtime.css" description="" isDevelopment="false" isRuntime="true" />
<FileResource type="JS" file="googlemap.runtime.js" description="" isDevelopment="false" isRuntime="true" />
<!--FileResource type="JS" url="https://maps.google.com/maps/api/js?sensor=false" description="" isDevelopment="false" isRuntime="true" /-->
<FileResource type="JS" url="https://maps.googleapis.com/maps/api/js?key=SUA_CHAVE_DE_AUTENTICAÇÃO_AQUI&amp;libraries=visualization" description="" isDevelopment="false" isRuntime="true"  />
</UIResources>
</Widget>

Só consegui fazer funcionar para uma instância na nuvem, pois coloco a URL como autorizada. Não consegui autorizar o meu localhost.

No meu próximo post eu falo sobre a criação da aplicação, a configuração do Widget e como fica tudo isso!

PS: Pensando sobre esse approach acabei descobrindo uma falha de segurança: como coloco a chave de autenticação aí, no momento de runtime dá pra capturar essa chave usando o console do browser e usar para outra coisa. Na verdade, na configuração da chave eu digo quais são as URLs autorizadas, reduzindo o risco de uso indevido, mas me incomoda o fato de a chave estar exposta publicamente:





Cheers
Ewerton

Alterando uma extensão do ThingWorx - Criando funções em Runtime e fazendo as chamadas da API HeatmapLayer

Criando a função para a execução (Runtime)

Fala galera, em meu post anterior eu descrevi como criei as propriedades que serão consumidas nas chamadas da API em Runtime. Agora vou descrever como ficou a função que chama a API e alguns detalhes e dificuldades que tive. Fiquei surpreso em ver o tamanho do código depois que fiz uma limpeza: sofri tanto para deixar funcionando que nem parece que foi só isso que fiz. :)

Antes...

Também decidi deixar o código mais flexível e para isso criei mais duas propriedades: CustomLayerDataField, CustomLayerLocationField. Isso permitirá escolher qual o campo irá ser usado para os dados e para a localização, sem depender de um Datashape específico. Também criei outras propriedades para a chamada da API, mas isso é mais para configurações de raio, opacidade, etc.

Aqui como ficaram as propriedades:

'CustomLayerDataField': {
'description' : 'Field which will provide data for Custom Layer',
'isBindingTarget': true,
'isVisible': true,
'isEditable': true,
'defaultValue': 'Reading',
'sourcePropertyName': 'CustomLayerData',
'baseTypeRestriction': 'NUMBER',
'baseType': 'FIELDNAME'
},
'CustomLayerLocationField': {
'description' : 'Field which will provide data for Custom Layer',
'isBindingTarget': true,
'isVisible': true,
'isEditable': true,
'defaultValue': 'location',
'sourcePropertyName': 'CustomLayerData',
'baseTypeRestriction': 'LOCATION',
'baseType': 'FIELDNAME'
},

Vejam que há outras características importantes:
sourcePropertyName - relaciona a outra propriedade e define a lista de propriedades que serão exibidas;
baseTypeRestriction - restringe o tipo que será mostrado na lista.

Em outras palavras, ao relacionar-se os dados ao campo CustomLayerData, as propriedades desse campo estão disponíveis para seleção, mas apenas as propriedades do tipo selecionado.






Agora sim, a função para Runtime 

Ficou assim:

if (updatePropertyInfo.TargetProperty === 'CustomLayerData') {
var customLayerDataRows = updatePropertyInfo.ActualDataRows;
var nRows = customLayerDataRows.length;
var heatmapData=[];
for (var rowNumber = 0; rowNumber < nRows; rowNumber++) {
var row = customLayerDataRows[rowNumber];
heatmapData[rowNumber] = 
{location: new google.maps.LatLng(parseFloat(row[this.getProperty('CustomLayerLocationField')].latitude) , parseFloat(row[this.getProperty('CustomLayerLocationField')].longitude)), weight: parseFloat(row[this.getProperty('CustomLayerDataField')])};
}  
var heatMapDissipating = this.getProperty('heatMapDissipating');
var heatMapGradient = this.getProperty('heatMapGradient');
var heatMapOpacity= this.getProperty('heatMapOpacity')
var heatMapRadius=this.getProperty('heatMapRadius')
var heatmap = new google.maps.visualization.HeatmapLayer({
data: heatmapData,
dissipating: heatMapDissipating,
opacity: heatMapOpacity,
radius: heatMapRadius
});
heatmap.setMap(this.map); 
return;
}

Algumas considerações:

  • Para usar o valor de uma propriedade, usei o método getProperty, como em: this.getProperty('CustomLayerLocationField')
    • Nesse caso específico irei pegar o nome da coluna definida nesse campo para que seja usada como location no mapa.
  • No caso acima, o Google Maps necessita dos valores de lat e long separados, por isso ao consumir esses dados foi usado: this.getProperty('CustomLayerLocationField')].latitude
  • O bloco for está iterando todos os dados associados ao Widget e criando um vetor (heatmapData) para que esse seja passado na chamada da API. Esse vetor possui um Object LatLong do GoogleMaps
    • Na tag location, estão sendo passados os valores de lat e long, como mencionei antes.
    • Na tag weight está sendo passado o valor dos pontos que serão plotados, com seu devido peso. Quanto maior, mais "vermelho" é o mapa.
O resultado no Thingworx ficou assim:

Tive várias dificuldades, muitas delas em como acessar os dados de uma matriz (pegar a lat e long do campo location, por exemplo) e outras com sintaxe de chamadas.

Vou continuar trabalhando nessa alteração, mas acho que isso já dá pra começar! Espero que ajude!

Cheers!
Ewerton

PS.. Faltou falar de Autenticação, que cobri no meu próximo post



Tuesday, December 29, 2015

Alterando uma extensão do ThingWorx - Criando propriedades para o Widget

Criando  propriedades para o Widget do Google Maps

Como mencionei em meu post anterior, o ThingWorx possibilita o uso de elementos de interface pré criados, chamados Widgets, na criação das telas de interface, chamadas de Mashups.

Esses Widgets possuem propriedades e eventos que podem ser utilizados na criação da aplicação utilizando-se drag n' drop no momento da criação do Mashup.

No caso da extensão do Google Maps, o widget foi criado em Javascript e possui 2 arquivos principais. Iremos falar de um deles agora, o googlemap.ide e como alterar para adicionar os parâmetros necessários para a funcionalidade de Heatmaps.

Na sessão de propriedades do Widget, irei criar algumas das propriedades que penso em usar:

  1. Liga/Desliga o Layer de Heatmap - propriedade Boolean apenas para acionar a funcionalidade.
  2. Tipo de Layer: penso em fazer com que a alteração seja flexível, então irei adicionar opções para se encolher entre criar-se um layer KML, um layer usando a API HeatMap ou uma FusionTable
  3. Dados de Entrada: é uma matriz com os dados que irão popular o mapa caso utilize-se a API
Vou focar nessas 3 propriedades pois meu desenvolvimento agora será utilizando a API. Quando for desenvolver para Layer KML ou FusionTable precisarei de propriedades para passar esse tipo de dado.

Código para adicionar-se as propriedades:

this.widgetProperties = function () {
return {
'ShowCustomLayer': {
'description': 'Select if custom layer is used',
  'isBindingTarget': true,
'defaultValue': false,
'baseType': 'BOOLEAN'
},
'CustomLayerType': {
  'isBindingTarget': true,
  'description' : 'Select type of custom layer: Google HeatMap or Fusion Tables',
  'baseType': 'STRING',
  'defaultValue': 'HeatMap',
  'selectOptions': [
    { value: 'HeatMap', text: 'HeatMap' },
    { value: 'FusionTables', text: 'Fusion Tables' }
    ]
  },
'CustomLayerData': {
  'description' : 'Data to populate Custom Layer',
  'isBindingTarget': true,
  'isVisible': true,
  'baseType': 'INFOTABLE'
},
}

Claro que  o cabeçalho já existe devido à declaração das outras variáveis. Coloquei ele no código acima apenas para nos localizarmos onde fazer a alteração no código existente.

Nesse código há algumas definições interessantes:
O nome da propriedade está definido entre aspas simples no começo de cada bloco
isBindingTarget - define se a propriedade pode ser associada a alguma variável na IDE
defaultValue - qual será o valor default para a propriedade
baseType -  tipo da propriedade. Isso definirá qual tipo de variável poderá ser associado na IDE. Note que para uma matriz de valores o ThingWorx usa o tipo INFOTABLE
selectOptions - define quais as opções disponíveis para seleção na IDE. Os valores devem ser passados no formato JSON

Depois que o arquivo é alterado e atualizado no Thingworx, as propriedades do Widget Google ficam conforme a figura ao lado.
Dá pra notar que a variável Boolean ficou como um checkBox, a INFOTABLE ficou com o ícone de uma tabela e a variável String que dependia de uma lista ficou com as opções disponíveis para serem selecionadas.

Agora, com as propriedades já definidas no Widget, e como a propriedade isBindingTarget for definida como True,  é possível associar valores ou um conjunto de valores a elas. No exemplo abaixo, o resultado do serviço QueryDataTableEntries está sendo associado à propriedade CustomLayerData. Esses dados serão utilizados posteriormente quando em Runtime, para que o mapa de calor seja populado.


É isso pessoal, a parte de alteração do Widget para a IDE e configuração da aplicação está OK. Agora falta a parte de Runtime, que irei descrever no próximo post

Cheers
Ewerton




Usando a API do Google Maps para criar um Mapa de Calor: Usando a Função HeatMap (HelloWorld)

Fala Pessoal, tudo bem?

Como mencionei no post anterior eu decidi usar a função Heatmaps layers do Google Maps para servir como base para a criação de dashboards para o projeto SmartCitzen do Hacker Clube de São José dos Campos.

Minha maior preocupação na verdade é um problema bom para se resolver: como a API tem uma cota diária, se a aplicação foi muito utilizada há uma chance de exceder-se a cota. Não vou me preocupar com isso por enquanto.

Exemplo de uso da função HeatMaps Layer

No exemplo que peguei no Google, os dados são 'hardcoded'. Funcionou bem, mas como disse em meu post anterior, a intenção é que os dados sejam dinâmicos, portanto preciso de uma rotina para atualizá-los:

var heatmapData = [
{location: new google.maps.LatLng(37.782, -122.447), weight: 0.5},
new google.maps.LatLng(37.782, -122.445),
new google.maps.LatLng(37.752986, -122.403112),
new google.maps.LatLng(37.751266, -122.403355),
{location: new google.maps.LatLng(37.782, -122.443), weight: 2},
new google.maps.LatLng(37.782, -122.437),
{location: new google.maps.LatLng(37.782, -122.435), weight: 0.5},
{location: new google.maps.LatLng(37.785, -122.447), weight: 3},
{location: new google.maps.LatLng(37.785, -122.435), weight: 3}
];

var heatMapDissipating = this.getProperty('heatMapDissipating');
var heatmap = new google.maps.visualization.HeatmapLayer({


var heatMapGradient = this.getProperty('heatMapGradient');
var heatMapOpacity= this.getProperty('heatMapOpacity')
var heatMapRadius=this.getProperty('heatMapRadius')

data: heatmapData,
dissipating: heatMapDissipating,
opacity: heatMapOpacity,
radius: heatMapRadius
});
heatmap.setMap(this.map);  

O primeiro bloco são dos dados que serão plotados no mapa, o segundo são opções para configurar o mapa e o terceiro é a chamada propriamente dita. Para fazer os dados serem dinâmicos preciso fazer com que a variável heatmapData seja alimentada por dados dinâmicos.

Trabalhando com Widgets:

A plataforma que estou usando, o Thingworx, usa o conceito de Widgets: existe uma interface RAD (Rapid Application Development) que possui elementos pré criados para que possamos criar as interfaces de usuário usando pouco código e o  conceito de drag n' drop (olhem esse video para ter uma ideia)

O Thingworx já possui uma extensão para o Google Maps, porém essa extensão não usa a API do HeatMaps. Decidi usar essa extensão como base e adicionar a funcionalidade. Para fazer isso é necessário alterar 2 arquivos da extensão:

  1. googlemap.ide - possui as características do Widget, propriedades, etc - Será necessário alterá-lo para que a IDE no Thinworx possua as propriedades relacionadas à nova funcionalidade
  2. googlemap.runtime - define as funções e chamadas quando a aplicação está em execução - É nesse arquivo que criaremos as funções JavaScript para tratar chamar a função HeatmapLayer

Estou tentando fazer posts pequenos para que não sejam cansativos, então chega por aqui. Nos próximos post irei mostrar como alterei o arquivo de ide e o de runtime

Cheers
Ewerton






Saturday, December 12, 2015

Pausa no Hardware - Usando a API do Google Maps para criar um Mapa de Calor: Qual escolher?

Fala Pessoal,

Agora que fiquei em paz com o Intel Edison e estou começando uma pequena briga com o Raspberry Pi, vou dar uma pausa no hardware e focar um pouco no desenvolvimento de aplicações.

Como falei no post anterior, estou ajudando o pessoal do Hacker Clube de SJC a criar uma aplicação chamada SmartCitzen, que terá como objetivo criar uma plataforma para que makers, escolas e afins possam enviar dados coletados por seus dispositivos e depois ter uma visão consolidada, rodar ferramentas de análise, etc.

Para isso estamos usando o ThingWorx, plataforma de IoT da PTC. Entre os vários motivos para usar a plataforma, eu trabalho na PTC e sou um entusiasta da tecnologia - curioso - como alguns dizem! Como não tenho background como programador, o ThingWorx é uma mão na roda para acelerar o desenvolvimento e abstrair várias necessidades que não conheço muito - desenvolver em HTML, segurança dos dados, persistir os dados, etc.

Em outro post falo mais sobre ThingWorx!


Hoje vai ser sobre outra surra: usar a API do Google Maps para criar um HeatMap ou mapa de calor (?) para o SmartCitzen. A ideia é consolidar os dados como feito aqui:

O ThingWorx já possui uma extensão para o Google Maps mas essa função não está disponível na extensão provida pela PTC.

USANDO AS APIs

As APIs do Google Maps  possuem ao menos 2 formas para criar um Mapa de Calor

  1. Fusion Tables - é um produto experimental do Google que facilita esse tipo de trabalho. A grande vantagem é o HeatMap ser rodado no servidor e não no cliente, portanto a performance para um alto volume de dados será melhor. Como desvantagem, o fato de que os dados tenham que estar em uma Fusion Table no Google, ou seja, os dados tem que sair da plataforma (sic) e alterá-los dinâmicamente é difícil.
  2. Função Heatmap Layer - mais flexível, porém tem cotas para utilização e cãlculo do layer no cliente.
Vejam uma tabela de comparação retirada da documentação:

Heatmap Layer
Fusion Table Layer
A large number of data points may result in reduced performance.
More data points will have little impact on performance.
Able to customize the appearance of the heatmap by changing such options as: the color gradient, the radius of data points, and the intensity of each data point.
No ability to customize the appearance of the heatmap.
Able to control whether heatmap data dissipates at higher zoom levels or not.
All heatmap data will dissipate as you zoom in.
Data can be stored with your HTML, stored on a server, or calculated on the fly. Data can be changed at runtime.
All data must be stored in a Fusion Table. Data cannot be easily changed at runtime.

Decidi escolher a segunda opção para evitar ter que colocar os dados na Fusion Table. Na verdade vou colocar as 2 opções na extensão, mas meu foco será na Função Heatmaps.

No próximo post irei descrever meu "Hello World" usando a função HeatMaps.







Friday, December 11, 2015

Intel Edison - Trégua por enquanto, paz ainda longe!

Fala Pessoal,

Depois de uma briga enorme com o Edison para fazer o WiFi funcionar e também instalar algumas aplicações para rodar um agente Thingworx (mais sobre isso em outro post), consegui um pouco de paz!

Depois de pegar o jeito de fazer um flash no Edison e ter 99% de sucesso em fazê-lo, entrei em uma outra batalha para instalar o Node.js no Edison.

Fato era que a versão 0.8.1 da lib mraa que é instalada usando o npm  é incompatível com o node v0.12.7. Depois de quebrar a cabeça procurando uma forma de instalar uma versão anterior da lib, descobri que é relativamente simples:

root@edison:~# npm install mraa@0.8.0

irá instalar a versão anterior do node. js
Agora WiFi funcionando, node.js rodando com o mraa irei fazer o ThingWorx funcionar com o Edison. Já fiz alguns testes e estou tendo problemas com a tabela de pinos, então espero mais uma batalha.

Se der muito trabalho vou deixar o Edison de lado e focar no Raspberry Pi, boas ideias e um pessoal animado para um hackathon aqui em SJC!

Hoje foi rápido. Em breve irei fazer um post sobre o ThingWorx e começarei a postar regularmente sobre isso. Estamos usando ThingWorx para criar uma aplicação chamada SmartCitzen.

Keep it up!
Ewerton