Criando meu próprio servidor Linux na “nuvem”

by Cássio R Eskelsen 20. junho 2010 12:08

Como comecei a trabalhar com projetos em Java a pouco tempo comecei a sentir uma certa dificuldade para hospedar meus projetos. Não que não existem bons hosts java, mas estava começando a ficar preocupado com a dispersão das minhas coisas: eu tinha um provedor para os sites em asp.net, tenho projetos hospedados no Google App Engine e pagava um serviço para hospedar meus fontes SVN.

Somado à necessidade de poder fazer alguns testes mais hardcore, resolvi contratar um VPS (virtual private server) em Linux e centralizar nele os sites asp.net, java e os meus fontes SVN.

Você deve estar pensando: “como assim? sites asp.net em Linux?”. Sim, para quem ainda não conhece, isso é possível com o Projeto Mono que hoje é mantido pela Novell.

Vou relatar nesse post um pouco sobre minha aventura.

1a Etapa: escolher um serviço de host VPS

Eu sei que a qualidade dos serviços é diretamente proporcional ao custo da hospedagem, não vou enganar pensando que existe milagre, então o que procurei foi um equilíbrio entre preço e confiabilidade.

Existem muitos serviços de VPS nos Estados Unidos mas não me sentia muito seguro pois sei que alguns são revenda da revenda.

No Brasil eu conhecia o serviço da Locaweb. Eles disponibilizam um serviço que eles classificam como “Cloud Computing”, o que tecnicamente discordo que seja cloud. É apenas o antigo VPS com outro termo. Não é objetivo desse post explicar as diferenças entre VPS e Cloud, mas o que posso adiantar é que hoje os outros serviços de Cloud Computing que seguem a risca a definição são o Microsoft Azure e o Google App Engine.

Procurando por “opiniões cloud locaweb” no Twitter acabei encontrando uma referência ao serviço de “cloud” da tecla.com.br que vim a descobrir é um braço da http://www.alog.com.brALOG que conheço de longa data, desde a época da Comdomonio (o nome era assim mesmo, com “m”). Sempre gostei muito da qualidade do serviço deles e não tive dúvidas em fechar um VPS, ops, “cloud” com eles. Escolhi o pacote mais básico, que contempla 2 processadores de 300 Mhz, 512 de RAM e 40 Gb de disco. Fechei a compra umas 3 da tarde, paguei o boleto, enviei o comprovante via email e no final da tarde já estava com meu server no ar (obrigado ao pessoal do suporte que teve paciência com a minha ansiedade!)

Agora começava a…

2a. Etapa: configurações básicas

Eu escolhi um servidor com Ubuntu 9 pré-instalado, mas sem nenhum pacote adicional.

A primeira coisa que precisei fazer foi entrar no painel de controle deles e conectar via ssh através do applet java que está no painel. Essa é a única forma de se conectar inicialmente já que o server vem bloqueado por questões de segurança e você precisa alterar a senha para liberar o acesso remoto.

Liberado o acesso, pude começar a conectar remotamente via ssh.

Primeiramente instalei o famoso pacote Apache + PHP + MySql. Você pode ver um tutorial básico aqui: http://www.ubuntugeek.com/how-to-install-apache2-webserver-with-phpcgi-and-perl-support-in-ubuntu-server.html e aqui: http://www.vivaolinux.com.br/dica/Instalando-servidor-Apache-+-PHP-+-MySQL-+-phpMyadmin-+-noip-no-Ubuntu-6.10-Server

(nesse post não vou entrar em detalhes, mas vou deixar links com as informações)

Java

Para meus projetos em Java eu instalei o Tomcat, seguindo as dicas deste tutorial:

Installing Tomcat 6 on Ubuntu: http://www.howtogeek.com/howto/linux/installing-tomcat-6-on-ubuntu/

3a. Etapa: migrando o servidor de SVN

Até o momento utilizava o serviço da www.codespaces.com . O serviço é excelente, só estou tirando de lá por uma questão de centralização e racionalização de custos com hospedagem.

Para montar um servidor SVN segui os passos descritos nesse link: http://stackoverflow.com/questions/60736/how-to-setup-a-subversion-svn-server-on-gnu-linux-ubuntu. Como não tenho um certificado de segurança recebo sempre uma observação nos clientes de que o certificado não é confiável, vou tentar resolver isso mais adiante.

Servidor instalado, subi o backup dos repositórios do SVN que a codespaces disponibiliza.

Nas estações onde desenvolvo usei o comando Relocate para apontar para a nova localização e fazer uma migração transparente.

Ao fazer o primeiro commit no novo local descobri que a codespaces.com usa um hook de post-commit que acaba gerando um warning no novo local pois ele não vem no backup. Para resolver isso basta entrar em cada repositório no diretório hooks e comentar dentro do arquivo post-commit a linha que chama o hook.

4a. Etapa: instalando o Mono

Tentei instalar o Mono de várias formas.  O Ubuntu que é instalado pela Alog possui um Mono muito antigo.

Alternativamente pode-se tentar instalar via http://badgerports.org/o que comigo não funcionou muito bem.

Tentei instalar baixando o trunk dos fontes do Mono, mas como era de se esperar, existem muitos erros.

Por último, resolvi baixar os tarballs com fontes estáveis: http://ftp.novell.com/pub/mono/sources-stable/ as instruções para compilar você pode encontrar em “compiling mono from tarball” . Tenha paciência, o processo de compilação pode demorar bastante tempo dependendo da configuração da máquina.

image

5a. Etapa: migrando os sites

Essa etapa irá demorar um pouco. Meus sites atuais estão no BlogEngine e quero mudar para um engine próprio baseado em asp.net MVC e algum banco NoSQL.

Tags: , , ,

asp.net | Web

Android: Trabalhando facilmente com campos data no SQLite

by Cássio R Eskelsen 6. abril 2010 18:12

Ao trabalhar com os primeiros campos data no Sqlite para Android, percebi que a classe Cursor não possui um método do tipo getDate() como existem os getString, getInt, getLong, etc.
Você até pode retornar a data com getString() e depois converter para um tipo data através de algumas classes, mas o código acaba ficando muito extenso.

Uma forma rápida de contornar isso é armazenar a data em milissegundos em um campo do tipo long:

Exemplo de gravação na base SQLite:

SQLiteDatabase db = SQLiteHelper.getDataBase();
ContentValues values = new ContentValues();
values.put("datainicio", meuObjetoQualquer.getDataInicio().getTime());
db.insert(“nomeTabela”, null, values);

O método getTime() retorna a quantidade de milissegundos desde 01/01/1970

Exemplo de leitura da base SQLite:

int id = 1;
String sql = "select id, datainicio from tabela";
Cursor c = db.rawQuery(sql, new String[] { Integer.toString(id) });
if (c.getCount() > 0) {
   c.MoveToFirst();
   MinhaClasse meuObjeto = new MinhaClasse();
   meuObjeto.setDataInicio(new Date(c.getLong(1)));
}

Como imaginado, instanciando um objeto do tipo Date passando como parâmetro a quantidade de milissegundos, automaticamente temos o valor convertido.

Deixo bem claro que é uma solução que jamais adotaria em uma base de dados "corporativa" pois isso dificultaria a legibilidade dos dados por outros sistemas. No entanto, em uma aplicação Android apenas nossa aplicação irá acessar os dados e com a falta de um método específico para retornar um campo Date, a solução acima se torna elegante.

Tags: ,

Android

Gerenciando as versões de bases Sqlite no Android

by Cássio R Eskelsen 6. abril 2010 17:22

No meu post anterior eu demonstrei como trabalhar com arquivos SQLite sem ter um contexto associado, ou seja, sem estar amarrado à uma Activity específica.

Isso acabou me trazendo uma consequência inesperada que é a impossibilidade de utilizar a classe android.database.sqlite.SQLiteOpenHelper que contém dois eventos interessantes: "onCreate" que é disparado quando um database é criado pelo método "openOrCreate" e o evento "onUpdate" que é disparado quando deve ser procedida uma atualização do banco de dados.

O evento de atualização de base ocorre quando é definida uma versão para o banco de dados maior que a atual, através do método database.setVersion(numero_da_versao)

Sem o SQLiteOpenHelper qual será nossa estratégia?

Tomando como base a classe SQLiteHelper do post anterior temos a seguinte linha de código:

SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(path+databaseName, null);

Essa linha tenta abrir o banco de dados. Se não encontrar o arquivo definido nas varíaveis "path+databaseName", cria o arquivo com estrutura zerada e versão definida como 0(zero).

O que faremos é, logo após essa linha, chamar um método que verificará/atualizará a estrutura do banco novo com o comando:

initialize(db);

Para evitar que essa verificação seja feita a cada vez que precisamos acessar o banco, criaremos uma variável estática na classe : private static boolean initialized = false; . Uma variável estática tem escopo de aplicação, ou seja, manterá seu valor enquanto a aplicação estiver no ar. Então, alteraremos a linha acima para:

if (!initialized) {
    initialize(db);
}

O código abaixo será um tanto prolixo para facilitar o entendimento:

	
    private static void initialize(SQLiteDatabase db) {
	int versao = db.getVersion();
	Log.v("database","Versão inicial: "+Integer.toString(versao));
	if(versao<1){
	   try{
	      RodaScripts(ScriptsVersao1(),db);
	      db.setVersion(1);
	      Log.v("database","Criada estrutura inicial");
	   }catch(Exception e){
	      Log.e("database",e.getMessage());
	   }
	}
	if(versao<1){
	  try{
	      RodaScripts(ScriptsVersao2(),db);
	      db.setVersion(2);
	      Log.v("database","Alterada a estrutura do banco");
	   }catch(Exception e){
	      Log.e("database",e.getMessage());
	   }
	}		
	versao = db.getVersion();
	Log.v("database","Versão após script inicial: "+Integer.toString(versao));
	initialized =true;
    }
 

O que esse código faz é verificar versão por versão se o banco é compatível com a versão esperada pelo seu sistema. Perceba a utilização do método getVersion para pegar a versão atual do banco e setVersion para atualizar o número da versão.

ScriptsVersao1() e ScriptsVersao2() são métodos que retornam uma array de Strings com comandos para criação/alteração de estruturas de tabelas. Você pode também adicionar Inserts de massas de dados iniciais:

private static String[] ScriptsVersao1() {
   String[] scripts = new String[] {				
	"CREATE TABLE \"contatos\" ( "
      + "[id] integer primary key autoincrement, "
      + "[nome] VARCHAR(50), ",
	"CREATE TABLE \"contato\" ( "
      + "[id] integer primary key autoincrement, " 
      + "[idContato] integer,"
      + "[telefone] VARCHAR(12), "
   };
   return scripts;
}
	
private static String[] ScriptsVersao2() {
   String[] scripts = new String[] {				
   "alter table contato add column [datanascimento] datetime"
   };
   return scripts;
}

O método que "roda" os scripts é esse:

private static void RodaScripts(String[] scriptsVersao1, SQLiteDatabase db) {
   for(int i=0;i<scriptsVersao1.length;i++){
	String sql = scriptsVersao1[i];
	db.execSQL(sql);            
   }        
}

E como vamos usar esse código?

Vamos usá-lo direto em uma activity para exemplificar:

package com.acme.ExemploVersaoBD;

import com.acme.ExemploVersaoBD.SQLite.SQLiteHelper;

import android.app.Activity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;

public class MainActivity extends Activity { 
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		SQLiteDatabase db = SQLiteHelper.getDataBase();
		try {
			String sql = "select * from contatos";
			Cursor c = db.rawQuery(sql, null);

			// (...) demais operações...
		} finally {
			db.close();
		}
		Log.v("database","Testando se nao esta inicializando novamente");
		SQLiteDatabase db2 = SQLiteHelper.getDataBase();
	}
}

Você pode verificar pelo LogCat que ele está funcionando conforme esperado:

log_eclipse

Verificando pelo Sqlite Expert Personal, podemos ver que a estrutura criada é a esperada:

image

Você pode baixar o exemplo completo no link:

Esse exemplo foi desenvolvido utilizando o Motodev Studio.

Tags: ,

Android

Android : Abrindo um database SQLite sem um Context

by Cássio R Eskelsen 1. abril 2010 14:17

Quem está começando a desenvolver com o SDK para Android já deve ter percebido que todos os exemplos de aplicações com banco de dados utilizam o Context de uma Activity para  abrir uma conexão com uma base SQLite, por exemplo:

  SQLiteDatabase db = ctx.openOrCreateDatabase("meubanco", Context.MODE_PRIVATE, null)

Isso funciona perfeitamente quando você pode deixar o código de acesso ao banco amarrado a uma Activity, mas é problemático quando você não tem um Context disponível – isso pode ocorrer, por exemplo, quando você deseja separar sua aplicação em camadas.

Você pode abrir um banco de dados utilizando o método “openOrCreateDatabase” da própria classe SQLiteDatabase, mas para isso você terá que informar o caminho completo do arquivo com os dados.

Os arquivos SQLite do Android ficam na pasta /data/data/package_da_aplicacao/databases/nomeDoArquivo

Tendo isso em mente, podemos criar uma classe helper para retornar uma instância de SQLiteDatabase:

package br.com.bizness.MinhaApp.Repositories.SQLite;

import android.app.Application;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class SQLiteHelper  {
	
	private static String path ="/data/data/br.com.bizness.MinhaApp/databases/";
	private static String databaseName = "nome_do_database";
	
	public static SQLiteDatabase getDataBase(){
		 
		SQLiteDatabase db = 
SQLiteDatabase.openOrCreateDatabase(path+databaseName, null); return db; } }

Obviamente que "br.com.bizness.MinhaApp" é o caminho da minha aplicação e você deve alterar para refletir sua situação.

Android…

Bom, esse é meu primeiro post sobre desenvolvimento para Android. Provavelmente virão muitos pela frente ainda, pois estou apenas começando no desenvolvimento para Android.
Minhas primeiras experiências estão sendo altamente positivas. Sinto um pouco de dificuldade pelo fato de vir do mundo .Net, mas o SDK em si é fantástico.
Como IDE, estou usando o MOTODEV Studio da Motorola, que é baseado em Eclipse.

Tags: , ,

Android

Nova ferramenta publicada : Ruas atingidas por enchentes

by Cássio R Eskelsen 24. fevereiro 2010 21:35

Dando continuidade à série de ferramentas que estou criando para acompanhamento de crises climáticas, publiquei recentemente o site Cotas Enchentes Blumenau. Esse site mostra quais as ruas são atingidas a cada nível de excesso no principal rio que cruza a cidade. O site permite simular vários níveis diferentes.

Um detalhe importante: esse site foi desenvolvido utilizando Java no back-end! Apesar de já ter contato com o Java de uma forma ou de outra desde 1998, foi o primeiro sistema sério que desenvolvi utilizando ele.

A relação de ruas com suas respectivas posições /cotas está hospedada no Google Fusion Tables. Esse é um serviço recente do Google que me deixou muito surpreso em termos de flexibilidade e performance.

O site está hospedado na estrutura de Cloud Computing da Google, o Google App Engine. Confesso que no começo apanhei um pouco para entender sua filosofia, mas passada a dificuldade inicial, estou extremamente satisfeito com o serviço.

O serviço está pronto para mostrar o nível do rio no momento, baseando no sistema SIBI/Furb, mas deixo isso desativado nos momentos em que não estamos passando por um enchente (espero nunca precisar ativar!).

Espero que o serviço seja útil.

Gestão/Acompanhamento de crises é um assunto de meu interesse. Se você tem alguma idéia de aplicativo nessa área, por favor, sinta-se a vontade para sugerir!

Tags:

Geo | Gestão de Crises | Google Maps

Criando eventos em objetos Javascript

by Cássio R Eskelsen 2. janeiro 2010 09:53

Uma necessidade muito comum na programação orientada a objetos é o disparo de trechos de código quando determinado evento acontece.
Usamos isso no dia-a-dia sem pensar muito, tomando como exemplo o evento onClick dos botões ou o evento onLoad da página.

Mas e quando criamos nossos próprios objetos javascript como adicionamos eventos a eles? Bom, existem várias maneiras e nesse post vou mostrar uma forma muito fácil que utiliza a bilbioteca YUI da Yahoo.

Primeiramente vamos criar o nosso objeto em um arquivo a parte chamado MeuObjeto.js:

function MeuObjeto(){
    this.OnFinish = new YAHOO.util.CustomEvent('Terminou');
    this.Start = start;
}

function start(){
    this.onFinish.fire();
}
Leia mais...

Tags:

Web

Usando o Google Maps para exibir o clima no Brasil

by Cássio R Eskelsen 5. dezembro 2009 18:00

Por indicação do Djonatas Tenfen achei esse link que descreve como exibir a previsão do tempo na Europa usando o Bing Maps.

Com esse artigo pretendo mostrar como fazer para exibir o clima no Brasil, obviamente usando o Google Maps!

A primeira dificuldade é encontrar um serviço com o clima em todo o Brasil e que seja relativamente fácil de ser utilizado (sem necessidade de fazer engenharia reversa de páginas web). Por sorte acabei encontrando o serviço do INPE (instituto Nacional de Pesquisas Espaciais) que traz a previsão em formato RSS.

1. Criando a Aplicação

Vou criar um projeto ASP.NET MVC utilizando o Visual Studio 2008. Utilizarei um template para substituir o layout default e não perder muito tempo com layout.

O foco desse artigo é o Google Maps e não o C#/.Net então não entrarei em muitos detalhes em relação à parte server do projeto (a não ser o tratamento do RSS)

2. Inserindo o Google Maps

Para cada projeto Google Maps você precisa solicitar uma chave para utilizar a API do Google Maps (a partir da versão 3 da API isso não será mais necessário). Essa chave é relativa à URL do servidor, o que significa que você precisa de uma chave para o desenvolvimento local e outra para a publicação.

Para facilitar a utilização da chave correta criei uma classe helper que analisando a URL do servidor pega a chave correta do web.config:

using System.Configuration;
using System.Web;

namespace Weather.Helpers
{
    public class GMapsApi
    {
        public static string GetMapsAPI()
        {
            if (HttpContext.Current.Request.Url.Host.ToLower().Contains("localhost"))
            {
                return ConfigurationManager.AppSettings["GMapsLocalKey"];
            }
            else
            {
                return ConfigurationManager.AppSettings["GMapsPublicKey"];
            } 
        }
    }
}

Obviamente no web.config preciso configurar essas duas chaves:

<appSettings>
  <add key="GMapsLocalKey" value="sua chave para localhost"/>
  <add key="GMapsPublicKey" value="sua chave para o servidor onde irá publicar"/>
</appSettings>

O primeiro passo para inserir um mapa do Google Maps é chamar o javascript com a API do Google Maps. Isso deve ser feito colocando a seguinte chamada na tag <head> da página:

<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;
key=
<%=Weather.Helpers.GMapsApi.GetMapsAPI() %>&sensor=true" type="text/javascript"></script>
Os parâmetros são:

v=2: indica que estamos usando a versão 2 da API do Google Maps;
key: sua chave da API. Perceba que estou chamando aqui a minha função que determina qual a chave a ser utilizada (na versão 3 do Google Maps ela não é mais necessária)
sensor=true: indica que o Google Maps deve tentar determinar a posição do usuário (indicado para aplicações mobile)

Procure chamar a API apenas onde ela será utilizada. Por isso não indico colocar no site.master de suas aplicações ASP.NET MVC. O mais indicado é usar o placeholder HeadContent.

Irei criar uma função javascript de nome initialize() que deve instanciar o objeto mapa e definir qual elemento DOM que conterá o mapa. Normalmente o elemento DOM é uma DIV.

Dentro do place holder MainContent irei adicionar um elemento div:

<div id="map" style="width: 100%; height: 750px;"></div> 
Colocar uma div ocupando 100% da altura merece um post a parte, então irei fixar aqui em 750 pixels de altura!

Agora já podemos definir a função initialize():

<script type="text/javascript" >
var mapa; function
initialize() { var map = new GMap2(document.getElementById("map")); mapa.setCenter(new GLatLng(-16.684185, -50.28125), 5); mapa.addControl(new GLargeMapControl()); mapa.enableScrollWheelZoom(); mapa.setMapType(G_DEFAULT_MAP_TYPES[2]); } </script>
Agora a brincadeira começou a ficar interessante!

A 2a. linha cria uma variável de nome “mapa”. Criamos ela fora de initialize para que tenhamos escopo na página
A 4a. linha inicializa um objeto mapa dentro da div “map”
A 5a. define a posição central inicial (calculei as coordenadas para que sejam exibidas a maioria das capitais brasileiras em uma resolução de 1024x768). O valor “5” define o zoom inicial.
A 6a. linha insere no mapa o controle que permite alterar o zoom e os botões que fazem o “pan” para os lados e para cima e para baixo.
A 7a. linha informa que deve ser habilitado o zoom in/ou com a rodinha do mouse.
A 8a. linha define que o tipo inicial do mapa deve ser o híbrido.

Quando iremos chamar a função initialize? Usaremos o jQuery para chamá-la apenas quando toda a página estiver montada.

No final, a página ficará com esse código:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage" %> <asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server"> Tempo no Brasil </asp:Content> <asp:Content ID="head" ContentPlaceHolderID="HeadContent" runat="server"> <script src="../../Scripts/jquery-1.3.2.js" type="text/javascript"></script> <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;
key=
<%=Weather.Helpers.GMapsApi.GetMapsAPI() %>&sensor=true" type="text/javascript"></script> <script type="text/javascript"> var mapa; function initialize() { mapa = new GMap2(document.getElementById("map")); mapa.setCenter(new GLatLng(-16.684185, -50.28125), 5); mapa.addControl(new GLargeMapControl()); mapa.enableScrollWheelZoom(); mapa.setMapType(G_DEFAULT_MAP_TYPES[1]); } </script> </asp:Content> <asp:Content ID="body" ContentPlaceHolderID="BodyPlaceHolder" runat="server"> onUnload="GUnload()" </asp:Content> <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server"> <div id="map" style="width: 100%; height: 780px;"> </div> <script type="text/javascript"> $(document).ready(function() { initialize(); }); </script> </asp:Content>

Perceba que estou chamando a função GUnload() da própria API do Google Maps que tem como objetivo limpar da memória todo o mapa assim que o usuário sair da página, diminuindo dessa forma a chance de eventuais problemas com gasto excessivo de memória.

O resultado será esse:

image

3. Construindo o “Banco de Dados” de cidades

Como o objetivo desse post é mostrar a construção do mapa, vou usar um modelo de dados bem simples, colocando em uma lista de memória as capitais dos estados do Brasil.

Obviamente você pode estender esse modelo colocando as maiores cidades, gravando os dados em um banco de dados, etc.

Para o exemplo irei usar a classe abaixo, contendo o ID da cidade no INPE, o nome da capital, a sigla do estado e as coordenadas geográficas da cidade.

using System.Collections.Generic;

namespace Weather.Models
{
    public class City
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public decimal Latitude { get; set; }

        public decimal Longitude { get; set; }

        public string StateAcronym { get; set; }

        public static List<City> GetCities()
        {
            List<City> cities = new List<City>();

            cities.Add(new City() { Id = 238, Name = "Porto Velho", StateAcronym = "RO",
                Latitude = -8.7624674766M, Longitude = -63.9044876278M });
            cities.Add(new City() { Id = 240, Name = "Rio Branco", StateAcronym = "AC",
                Latitude = -9.9754648208M, Longitude = -67.8105087280M });
            cities.Add(new City() { Id = 234, Name = "Manaus", StateAcronym = "AM",
                Latitude = -3.1024484634M, Longitude = -60.0254669189M });
            cities.Add(new City() { Id = 223, Name = "Boa Vista", StateAcronym = "RR",
                Latitude = 2.8195800781M, Longitude = -60.6734657287M });
            cities.Add(new City() { Id = 221, Name = "Belém", StateAcronym = "PA",
                Latitude = -1.4564432713M, Longitude = -48.5043983304M });
            cities.Add(new City() { Id = 232, Name = "Macapá", StateAcronym = "AP",
                Latitude = 0.0385661366M, Longitude = -51.0664177261M });
            cities.Add(new City() { Id = 236, Name = "Palmas", StateAcronym = "TO",
                Latitude = -10.1674923853M, Longitude = -48.3334037018M });
            cities.Add(new City() { Id = 243, Name = "São Luís", StateAcronym = "MA",
                Latitude = -2.5304510629M, Longitude = -44.3033714168M });
            cities.Add(new City() { Id = 245, Name = "Teresina", StateAcronym = "PI"
                , Latitude = -5.0894684783M, Longitude = -42.8023605331M });
            cities.Add(new City() { Id = 229, Name = "Fortaleza", StateAcronym = "CE",
                Latitude = -3.7174611763M, Longitude = -38.5433273026M });
            cities.Add(new City() { Id = 235, Name = "Natal", StateAcronym = "RN",
                Latitude = -5.7954773774M, Longitude = -35.2093048098M });
            cities.Add(new City() { Id = 231, Name = "João Pessoa", StateAcronym = "PB",
                Latitude = -7.1154866001M, Longitude = -34.8633003854M });
            cities.Add(new City() { Id = 239, Name = "Recife", StateAcronym = "PE",
                Latitude = -8.0544929496M, Longitude = -34.8813018808M });
            cities.Add(new City() { Id = 233, Name = "Maceió", StateAcronym = "AL",
                Latitude = -9.6665029515M, Longitude = -35.7353096042M });
            cities.Add(new City() { Id = 220, Name = "Aracaju", StateAcronym = "SE",
                Latitude = -10.9115095226M, Longitude = -37.0723190324M });
            cities.Add(new City() { Id = 242, Name = "Salvador", StateAcronym = "BA",
                Latitude = -12.9715194702M, Longitude = -38.5113372802M });
            cities.Add(new City() { Id = 222, Name = "Belo Horizonte", StateAcronym = "MG",
                Latitude = -19.8175430307M, Longitude = -43.9563903769M });
            cities.Add(new City() { Id = 246, Name = "Vitória", StateAcronym = "ES",
                Latitude = -20.3195533734M, Longitude = -40.3383636479M });
            cities.Add(new City() { Id = 241, Name = "Rio de Janeiro", StateAcronym = "RJ",
                Latitude = -22.9035564806M, Longitude = -43.2083928627M });
            cities.Add(new City() { Id = 244, Name = "São Paulo", StateAcronym = "SP",
                Latitude = -23.5485496519M, Longitude = -46.6364212029M });
            cities.Add(new City() { Id = 227, Name = "Curitiba", StateAcronym = "PR",
                Latitude = -25.4285487993M, Longitude = -49.2734451079M });
            cities.Add(new City() { Id = 228, Name = "Florianópolis", StateAcronym = "SC",
                Latitude = -27.5975551455M, Longitude = -48.5494461504M });
            cities.Add(new City() { Id = 237, Name = "Porto Alegre", StateAcronym = "RS",
                Latitude = -30.0335502624M, Longitude = -51.2304801940M });
            cities.Add(new City() { Id = 225, Name = "Campo Grande", StateAcronym = "MS",
                Latitude = -20.4435195922M, Longitude = -54.6464691162M });
            cities.Add(new City() { Id = 226, Name = "Cuiabá", StateAcronym = "MT",
                Latitude = -15.5965022580M, Longitude = -56.0974616760M });
            cities.Add(new City() { Id = 230, Name = "Goiânia", StateAcronym = "GO",
                Latitude = -16.6795196708M, Longitude = -49.2544251117M });
            cities.Add(new City() { Id = 224, Name = "Brasília", StateAcronym = "DF",
                Latitude = -15.7805194860M, Longitude = -47.9304084804M });

            return cities;
        }
    }
}
4. Buscando as informações do INPE

A partir daqui entramos no território sem lei dos XMLs.
Temos que buscar o RSS das cidades desejadas e salvar localmente. Depois temos que ler os XMLs e extrair as informações desejadas.

Criei um helper para efetuar esse download (\Helpers\INPEHelper.cs). A parte principal do helper é esta:

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Web;
using Weather.Models;
using System.Text;

namespace Weather.Helpers
{
    public class INPEHelper
    {
        private string URL = "http://servicos.cptec.inpe.br/RSS/cidade/{0}/previsao.xml";
        public INPEHelper()
        {
        }

        public List<Forecast> GetForecasts(List<City> cities)
        {
            List<Forecast> forecs = new List<Forecast>();

            foreach (City city in cities)
            {
                GetRSS(city.Id);
                RSS rs = RSS.Load((HttpContext.Current.Server.MapPath("~/app_data/" + 
string.Format("{0}.xml", city.Id)))); var dataXml = DateTime.Parse(rs.channel.item[0].pubDate[0]); var previsao = rs.channel.item[0].description[0]; var imagem = previsao.Substring(previsao.IndexOf("<img"),
previsao.IndexOf("/>") - 1); Forecast fore = new Forecast(); fore.City = city; fore.ForecastImageUrl = imagem; fore.ForecastTemperature = previsao.Substring(previsao.IndexOf("</b>") + 8); fore.ForecastTemperature = fore.ForecastTemperature.
Remove(fore.ForecastTemperature.IndexOf("<br>")); string data = previsao.Substring(previsao.IndexOf("<br>") + 4); data = data.Remove(data.IndexOf("</b>")); fore.ForecastDate = GetForeDate(dataXml, data); File.Delete(HttpContext.Current.Server.MapPath("~/app_data/" +
string.Format("{0}.xml", city.Id))); } return forecs; } private void GetRSS(int cityCode) { DownloadFile(string.Format(URL, cityCode.ToString()),
HttpContext.Current.Server.MapPath("~/app_data/" + cityCode.ToString() + ".xml")); }

O método DownloadFile está nesse mesmo fonte.

Estou salvando os xml no app_data pois é uma pasta que normalmente tem permissão de gravação.

Um detalhe importante: para pesquisar no XML do RSS eu estou usando o Linq to XML. Já fiz um post tempos atrás sobre como utilizar esse framework. De qualquer forma já estará tudo pronto para você utilizar no código que irei disponibilizar para download. Você pode tratar o XML direto também, existem várias alternativas.

O RSS do INPE retorna a previsão do dia de amanhã (ou de hoje) e mais alguns dias para frente. A previsão de amanhã (ou de hoje) é dada através de uma imagem. As demais através de uma string. Por hora, irei pegar apenas a previsão de amanhã (ou de hoje) e exibir direto a imagem.

5. Exibindo as informações no Mapa

Agora que passamos a parte mais chatinha, falta apenas exibir os dados no mapa, o que será muito fácil de fazer em se tratando de Google Maps!

Utilizaremos a classe GIcon da API do Google Maps para criar um marcador personalizado com a imagem da previsão e a classe GMarker para colocar a previsão no mapa.

Poderíamos fazer o controller Index retornar uma lista de objetos Forecast e iterarmos sobre essa lista.  Mas aqui teríamos um problema: o objeto mapa pode não estar criado ainda e você irá tentar adicionar marcadores em um objeto nulo. Então para garantir que não teremos problemas iremos invocar os dados apenas após a chamada do método initialize:

   1:      <script type="text/javascript">
   2:          $(document).ready(function() {
   3:              initialize();
   4:   
   5:   
   6:              var url = "/Home/GetForecasts";
   7:              $.getJSON(url, null, function(data) {
   8:                  $.each(data, function(index, forec) {
   9:                      var forecIcon = new GIcon(G_DEFAULT_ICON);
  10:                      forecIcon.image = forec.ForecastImageUrl;
  11:                      forecIcon.iconSize = new GSize(73, 59);
  12:                      forecIcon.shadow = "http://maps.google.com/mapfiles/water.gif";
  13:                      forecIcon.shadowSize = new GSize(83, 59);
  14:                      markerOptions = { icon: forecIcon, title: forec.City.Name + '/' + 
forec.City.StateAcronym + ' Temperatura: ' + forec.ForecastTemperature };
  15:                      var point = new GLatLng(forec.City.Latitude, forec.City.Longitude)
  16:                      mapa.addOverlay(new GMarker(point, markerOptions));                  
  17:   
  18:                  });
  19:              });
  20:   
  21:          });
  22:   
  23:      </script>

A linha 7 utiliza ajax para chamar um método no servidor que retornará um json com os dados. O método está no controller Home (mais abaixo colo ele)

A partir da linha 8 eu itero sobre cada elemento json que retornou.

Nas linhas 9,10,11,12,13 eu crio um icone personalizado contendo a imagem do INPE. Como a imagem vai ficar meio ilegível, apliquei um fundo (o resultado final não é dos mais belos, mas serve para nossos fins didáticos).

Na linha 14 crio uma array contendo o ícone personalizado e um título contendo o nome da cidade e a temperatura.

Por último, nas linhas 15 e 16 eu crio um marcador utilizando a latitude e longitude da cidade e o ícone personalizado.

O resultado final é esse:

image

No link você pode baixar todo o código fonte da solução. Apenas não esqueça de gerar sua chave de API no Google Maps!

Qualquer dúvida, deixe um comentário!

Tags: , , , ,

Google Maps

IIS Smooth Streaming

by Cássio R Eskelsen 28. novembro 2009 13:01

O IIS Smooth Streaming é uma das maiores novidades que surgiram em termos de distribuição de mídia na internet, seja para mídia ao vivo, seja sob demanda.  Particularmente achei a tecnologia tão fantástica que até resolvi escrever um post sobre ela em meu blog, sendo que é um assunto que foge do escopo usual do blog.

Descobri a tecnologia assistindo ao vivo os Keynotes da PDC 2009, que é um evento da Microsoft destinado a desenvolvedores. Os keynotes foram assistidos por milhares de pessoas no mundo todo, durante o horário comercial, e mesmo assim a transmissão fluiu muito bem, sem bufferizações e sem gargalos.
O player funcionava como um legítimo TIVO, ou seja, você podia pausar a qualquer momento e voltar para qualquer ponto. Já assisti muitas transmissões ao vivo usando o Adobe Flash e nunca tive a mesma experiência.

Você pode experimentar a tecnologia vendo essa transmissão on demand: http://www.iis.net/media/experiencesmoothstreaming

Atenção!! Você pode ver também através do seu IPhone!! http://www.iis.net/iphone

Mas o que é o IIS Smooth Streaming afinal?

O IIS SS é uma tecnologia de transmissão de vídeo “adaptativa”, ou seja, ela analisa constamente as condições da sua banda larga e a performance local de renderização de vídeo para transmitir o vídeo na melhor qualidade possível sem gargalar.

Se você tiver uma boa banda larga (superior a 3 Mbits, o que não é raro hoje em dia) e uma placa de vídeo razoável, você será capaz de ver vídeo em Full HD 1080p!

Requerimentos

Para funcionar, a tecnologia depende dos seguintes componentes:

Do lado cliente:
- Plugin do Silverlight

Do lado servidor:
- Windows Vista SP1 ou Windows 7 ou Windows Server 2008
- IIS (servidor WEB que normalmente já vem com o sistema operacional, bastando ativá-lo)
- IIS Media Services: a extensão do IIS que faz o “milagre”. Existem versões de 32 e 64 bits

Produção:
- Os vídeos devem ser convertidos para um formato específico usando-se o Microsoft Expression Encoder

Montando um servidor local de mídia

Se você possuir um computador com Vista, Windws 7 ou 2008, basta baixar e instalar o IIS Media Server conforme indicado acima (versão 32 ou 64 bits, dependendo da versão do seu servidor).

Caso ainda não tenha feito, não esqueça de ativar o Servidor IIS antes de prosseguir. Você pode fazer isso indo em Painel de Controle->Programas->Ativar/Desativar recursos do Windows.

Se a instalação tiver sido feita com sucesso, você terá alguns novos ícones no gerenciador do IIS:

image

Caso você não tenha o Expression Encode e quiser fazer apenas um teste, você pode baixar o vídeo Big Bunny Video já convertido para o formato do Smooth Streaming. Baixe os dois arquivos disponíveis no link.

Uma vez baixado, descompacte os arquivos em algum diretório qualquer ou direto dentro do diretório do IIS (c:\inetpub\wwwroot).
Se você não colocou no diretório default terá que criar um diretório virtual no IIS. Para isso, no gerenciador do IIS, clique com o botão direito sobre Default Web Site e selecione Add Virtual Directory. Preencha conforme abaixo, substituindo pelo caminho físico correto:

image

Agora a demonstração já está disponível no link http://localhost/bunny/default.html, mas antes de abrir, altere o arquivo default.html adicionando o player que você acabou de baixar (o arquivo menor), conforme abaixo:

<div> Silverlight Player: 
    <select id="SilverlightPlayer">
        <option value="SmoothStreamingBlackGlass.xap" selected>Black Glass (Default)</option>
        <option value="SmoothStreamingUXSimulator.xap">User Experience Simulator</option>
        <option value="SmoothStatsTracker.xap">Sample Client Refresh</option>
    </select>
</div>

Pronto! Agora abra o site (não clique direto em default.html, você deve acessar através de http para que o IIS e a extensão intermediem o vídeo para você).

image

Na página que irá abrir, selecione a opção SAMPLE CLIENT REFRESH e clique em Start Playing!.

image

Veja que por default essa demonstração traz algumas informações adicionais sobre o vídeo, como por exemplo, a banda máxima de download possível e a banda sendo consumida no momento (no canto superior direito). Obviamente, como é um video local, estaremos usando a banda máxima. O interessante é que você pode ajustar dinamicamente a banda consumida!

O gráfico no canto inferior esquerdo mostra os bit rates disponíveis e qual está sendo usado no momento (no servidor o arquivo é replicado em vários bit rates diferentes, como você pode perceber no arquivo contendo o exemplo de mídia codificada.

Opinião

O IIS Smooth Streaming possui qualidades suficientes para possibilitar a transmissão de TV via internet, inclusive ela foi criada inicialmente para transmissão das Olimpíadas através de um site americano.

Juntamente com a popularização do Silverlight provavelmente veremos cada vez mais conteúdo sendo disponibilizado nesse formato.

Tags: , , ,

Silverlight

Alertas Climáticos SC

by Cássio R Eskelsen 29. setembro 2009 03:11

Com objetivo de ajudar quem quer informações sobre o nível dos rios e outros dados climáticos sobre a região do Alto Vale do Itajaí, estou criando alguns twitters automatizados, que de hora em hora trazem a situação dos rios atualizada:

http://twitter.com/alertasblumenau
http://twitter.com/alertasbrusque (em fase de implantação)
http://twitter.com/alertasindaial (em fase de implantação)

Os dados vem do do SIBI (Sistema de Informações da Bacia do Itajaí ),mantido pela FURB.

(Ps aos colegas de profissão: o serviço roda em asp.net)

Tags:

Geo | Gestão de Crises

MSN Next, o MSN em Silverlight

by Cássio R Eskelsen 14. maio 2009 21:05

Recebi ontem via Microsoft Connect o convite para ser beta tester do novo MSN.com.

O site está disponível em http://next.br.msn.com .Não sei se está disponível apenas para convidados.

O  interessante do site é que agora ele está rodando em Silverlight para quem tem o player instalado.

Veja abaixo como ficou:

image

Veja na área apontada pela seta branca que existem janelas com destaques. Essas janelas são “rotatórias”, ou seja, quando você clica em uma janela que está atrás, ela vem para frente.

Na área delimitada pelo frame vermelho existem widgets que mostram as ultimas mensagens do hotmail, as últimas atualizações do Messenger e a previsão do tempo. Acredito que no futuro possa se colocar mais widgets.

Parece que está ficando interessante, mas por enquanto me lembra muito o Windows Mobile: por mais que se faça uma cara bonitinha, o passado está atras ainda. Por exemplo, quando você vai logar com seu usuário, você é redirecionado para aquela velha página de login em html, que é a mesma desde os tempos do MSN Passport.

Vamos torcer para que fique legal. Quanto mais opções de portais informativos, melhor!

Tags: