O uso de telefones celulares em todo o mundo está aumentando constantemente. Em 2013, cerca de 73% dos usuários da Internet consumiam conteúdo por meio de um dispositivo móvel e esse percentual deve atingir perto de 90% em 2017.
Existem, é claro, muitas razões para a revolução móvel. Mas um dos mais significativos é que os aplicativos móveis geralmente têm acesso a um contexto mais rico, já que quase todos os smartphones hoje são equipados com sensores de localização, sensores de movimento, bluetooth e wi-fi. Ao fazer uso de seus dados, os aplicativos podem alcançar “consciência de contexto” que pode aumentar drasticamente seus recursos e valor, e pode realmente fazer com que se destaquem nas lojas de aplicativos.
Neste tutorial, vamos explorar a criação de aplicativos sensíveis ao contexto por meio de um exemplo de processamento de eventos complexos. Usaremos um exemplo bastante simples: um aplicativo de preço de combustível que encontra os melhores preços de combustível em sua área.
Dentro Projetando Tecnologia Calma , Mark Weiser e John Seely Brown descreve tecnologia calma como 'aquilo que informa, mas não exige nosso foco ou atenção'.
Os aplicativos móveis sensíveis ao contexto são altamente consistentes com essa noção e são um passo importante e valioso neste caminho. Eles empregam informações contextuais coletadas de seus sensores para fornecer proativamente ao usuário informações valiosas e fazem isso com o mínimo de esforço por parte do usuário. Mark Weiser e John Seely Brown, sem dúvida, aplaudiriam esse avanço tecnológico.
Consciência de contexto é a ideia de que um aplicativo pode sentir e reagir com base nos dados contextuais aos quais tem acesso. Esse aplicativo usa dados ricos do sensor que estão disponíveis em um dispositivo móvel para fornecer informações precisas e relevantes ao usuário no contexto apropriado. Por meio de tendências que ele observa ao longo do uso do dispositivo e / ou por meio de feedback fornecido pelo usuário, esse aplicativo pode realmente “aprender” com o tempo, tornando-se assim “mais inteligente” e mais útil.
Processamento de eventos complexos (CEP) é uma forma de processamento de eventos que emprega análises mais sofisticadas de vários eventos (ou seja, ao longo do tempo, de fontes diferentes e assim por diante), integrando e analisando seu conteúdo para deduzir informações e padrões mais significativos.
Em um aplicativo móvel, o CEP pode ser aplicado a eventos gerados a partir dos sensores do dispositivo móvel, bem como a fontes de dados externas às quais o aplicativo tem acesso.
Para fins de nosso tutorial de processamento de eventos complexos, vamos supor que os recursos de nosso aplicativo de preço de combustível sejam limitados ao seguinte:
OK, vamos começar.
Vamos começar com a lógica para detectar automaticamente os locais de residência e trabalho do usuário. A fim de manter as coisas simples para nosso exemplo de processamento de eventos complexos, vamos supor que o usuário tem uma agenda de trabalho razoavelmente normal. Portanto, podemos presumir que o usuário estará em casa entre 2 e 3 da manhã e no escritório entre 2 e 3 da tarde.
Com base nessas suposições, definimos duas regras CEP e coletamos dados de localização e hora do smartphone do usuário:
O algoritmo de alto nível para detectar locais é descrito abaixo.
Vamos supor a seguinte estrutura de dados JSON simples para dados de localização:
ir para a estrutura de estratégia de mercado
{ 'uid': 'some unique identifier for device/user', 'location': [longitude, latitude] 'time': 'time in user's timezone' }
Nota: É sempre uma boa prática tornar os dados do sensor imutáveis (ou tipo de valor), para que possam ser usados com segurança por diferentes módulos no fluxo de trabalho do CEP.
Vamos implementar nosso algoritmo usando um padrão de módulo combinável , em que cada módulo executa apenas uma tarefa e chama a próxima quando a tarefa é concluída. Isso está em conformidade com o Unix Regra da Modularidade filosofia.
Especificamente, cada módulo é uma função que aceita um config
objeto e um next
função que é chamada para passar os dados para o próximo módulo. Consequentemente, cada módulo retorna uma função que pode aceitar dados do sensor. Aqui está a assinatura básica de um módulo:
// nominal structure of each composable module function someModule(config, next) { // do initialization if required return function(data) { // do runtime processing, handle errors, etc. var nextData = SomeFunction(data); // optionally call next with nextData next(nextData); } }
Para implementar nosso algoritmo para deduzir os locais de residência e trabalho do usuário, precisaremos dos seguintes módulos:
Cada um desses módulos é descrito com mais detalhes nas subseções a seguir.
Nosso filtro de tempo é uma função simples que recebe eventos de dados de localização como entrada e apenas passa os dados para o next
módulo se o evento ocorreu dentro do intervalo de tempo de interesse. O config
os dados para este módulo consistem, portanto, nas horas de início e término da fração de tempo de interesse. (Uma versão mais sofisticada do módulo pode filtrar com base em múltiplas fatias de tempo.)
Aqui está uma implementação de pseudocódigo do módulo de filtro de tempo:
function timeFilter(config, next) { function isWithin(timeval) { // implementation to compare config.start <= timeval <= config.end // return true if within time slice, false otherwise } return function (data) { if(isWithin(data.time)) { next(data); } }; }
A responsabilidade do acumulador é simplesmente coletar dados de localização para serem passados para o next
módulo. Esta função mantém um depósito interno de tamanho fixo para armazenar dados. Cada novo local encontrado é adicionado ao balde até que ele esteja cheio. Os dados de localização acumulados no intervalo são então enviados para o próximo módulo como uma matriz.
Dois tipos de baldes de acumulador são suportados. O tipo de intervalo afeta o que é feito com o conteúdo do intervalo depois que os dados são encaminhados para a próxima fase, da seguinte maneira:
Balde de janela oscilante (type = 'tumbling'
): após encaminhar os dados, esvazia todo o balde e começa do zero (tamanho do balde reduzido para 0)
Tipo de janela em execução (type = 'running'
): após encaminhar os dados, descarta apenas o elemento de dados mais antigo no intervalo (reduz o tamanho do intervalo em 1)
Aqui está uma implementação básica do módulo acumulador:
function accumulate(config, next) { var bucket = []; return function (data) { bucket.unshift(data); if(bucket.length >= config.size) { var newSize = (config.type === 'tumbling' ? 0 : bucket.length - 1); next(bucket.slice(0)); bucket.length = newSize; } }; }
Claro que existem muitas técnicas sofisticadas em geometria coordenada para agrupar dados 2D. Esta é uma maneira simples de agrupar dados de localização:
Aqui está uma implementação deste algoritmo de agrupamento (usando Lo-Dash
):
o que é s corp e c corp
var _ = require('lodash'); function createClusters(location_data, radius) { var clusters = []; var min_points = 5; // Minimum cluster size function neighborOf(this_location, all_locations) { return _.filter(all_locations, function(neighbor) { var distance = distance(this_point.location, neighbor.location); // maximum allowed distance between neighbors is 500 meters. return distance && (500 > distance); } } _.each(location_data, function (loc_point) { // Find neighbors of loc_point var neighbors = neighborOf(loc_point, location_data, radius); _.each(clusters, function (cluster, index) { // Check whether some of the neighbors belong to cluster. if(_.intersection(cluster, neighbors).length){ // Expand neighbors neighbors = _.union(cluster, neighbors); // Remove existing cluster. We will add updated cluster later. clusters[index] = void 0; } }); if(neighbors.length >= min_points){ // Add new cluster. clusters.unshift(neighbors); } }); return _.filter(clusters, function(cluster){ return cluster !== void 0; }); }
O código acima assume a existência de um distance()
função que calcula a distância (em metros) entre duas localizações geográficas. Ele aceita dois pontos de localização na forma de [longitude, latitude]
e retorna a distância entre eles. Aqui está um exemplo de implementação de tal função:
function distance(point1, point2) { var EARTH_RADIUS = 6371000; var lng1 = point1[0] * Math.PI / 180; var lat1 = point1[1] * Math.PI / 180; var lng2 = point2[0] * Math.PI / 180; var lat2 = point2[1] * Math.PI / 180; var dLat = lat2 - lat1; var dLon = lng2 - lng1; var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); var arc = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var distance = EARTH_RADIUS * arc; return distance; }
Com nosso algoritmo de clusterização definido e implementado (em nossa função createClusters()
mostrada anteriormente), podemos usá-lo como base para nosso módulo de clusterização:
function clusterize(config, next) { return function(data) { var clusters = createClusters(data, config.radius); next(clusters); }; }
Todas as funções do componente necessário estão definidas, então estamos prontos para codificar nossas regras de localização de casa / trabalho.
Aqui, por exemplo, está uma possível implementação da regra de localização residencial:
var CLUSTER_RADIUS = 150; // use cluster radius of 150 meters var BUCKET_SIZE = 500; // collect 500 location points var BUCKET_TYPE = 'tumbling'; // use a tumbling bucket in our accumulator var home_cluster = clusterize({radius: CLUSTER_RADIUS}, function(clusters) { // Save clusters in db }); var home_accumulator = accumulate({size: BUCKET_SIZE, type: BUCKET_TYPE}, home_cluster); var home_rule = timeFilter({start: '2AM', end: '3AM'}, home_accumulator);
Agora, sempre que os dados de localização são recebidos do smartphone (via websocket, TCP, HTTP), encaminhamos esses dados para home_rule
função que, por sua vez, detecta clusters para a casa do usuário.
A 'localização inicial' do usuário é então considerada o centro do cluster de localização inicial.
Observação: embora isso possa não ser totalmente preciso, é adequado para nosso exemplo simples, especialmente porque o objetivo deste aplicativo, em qualquer caso, é simplesmente conhecer a área ao redor da casa do usuário, em vez de saber a localização exata da casa do usuário.
Aqui está um exemplo de função simples que calcula o 'centro' de um conjunto de pontos em um cluster pela média das latitudes e longitudes de todos os pontos no conjunto de cluster:
function getCentre(cluster_data) { var len = cluster_data.length; var sum = _.reduce(cluster_data, function(memo, cluster_point){ memo[0] += cluster_point[0]; memo[1] += cluster_point[1]; return memo; }, [0, 0]); return [sum[0] / len, sum[1] / len]; }
Uma abordagem semelhante poderia ser empregada para deduzir o local de trabalho, com a única diferença sendo que ele usaria um filtro de tempo entre 14h e 15h (em vez de 2h e 3h).
Nosso aplicativo de combustível é, portanto, capaz de automaticamente detectar os locais de trabalho e casa do usuário sem exigir qualquer intervenção do usuário. Esta é a computação com reconhecimento de contexto no que há de melhor!
O trabalho árduo para estabelecer a percepção do contexto agora foi feito, mas ainda precisamos de mais uma regra para identificar quais preços de postos de combustível monitorar (ou seja, quais postos de combustível estão próximos o suficiente da casa do usuário ou local de trabalho para serem relevantes). Esta regra precisa de acesso a todos os locais de postos de combustível para todas as regiões suportadas pelo aplicativo de combustível. A regra é a seguinte:
Isso pode ser facilmente implementado usando a função de distância mostrada anteriormente como um filtro de localização a ser aplicado a todos os postos de combustível conhecidos pelo aplicativo.
Uma vez que o aplicativo de combustível obtém a lista de postos de combustível preferidos (ou seja, próximos) para o usuário, ele pode observar facilmente os melhores preços de combustível nesses postos. Ele também pode notificar o usuário quando um desses postos de combustível tiver preços ou ofertas especiais, especialmente quando for detectado que o usuário está perto desses postos.
Neste tutorial de processamento de eventos complexos, nós realmente mal tocamos a superfície da computação ciente de contexto.
Em nosso exemplo simples, adicionamos contexto de localização a um aplicativo de relatório de preços de combustível simples e o tornamos mais inteligente. O aplicativo agora se comporta de maneira diferente em cada dispositivo e, com o tempo, detecta os padrões de localização para melhorar automaticamente o valor das informações que fornece aos usuários.
Certamente, muito mais dados lógicos e de sensor podem ser adicionados para aumentar a precisão e a utilidade de nosso aplicativo sensível ao contexto. UMA desenvolvedor móvel inteligente poderia, por exemplo, fazer uso de dados de redes sociais, dados meteorológicos, dados de transações de terminais POS e assim por diante para adicionar ainda mais consciência de contexto ao nosso aplicativo e torná-lo mais viável e comercializável .
Com a computação com reconhecimento de contexto, as possibilidades são infinitas. Mais e mais aplicativos inteligentes continuarão a aparecer nas lojas de aplicativos que empregam essa tecnologia poderosa para tornar nossas vidas mais simples.