Fast CGI Web Role y Windows Azure SDK 1.3

En esta última versión del SDK hemos podido encontrar ciertos problemas con las aplicaciones de tipo Fast CGI, como por ejemplo esto:

Este error es debido a que, al crear una nueva aplicación con la nueva plantilla proporcionada por el SDK e intentar configurarla para Full IIS, no es capaz de encontrar el runtime de php y por lo tanto lanza una excepción, lo cual significa que por el momento las aplicaciones de tipo Fast CGI no soportan Full IIS :(

Para solventar este escenario únicamente debemos comentar el elemento Sites del archivo ServiceDefinition.csdef

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="PHPWindowsAzure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WebRole name="WebCgiRole_PHP" enableNativeCodeExecution="true">
    <!--<Sites>
      <Site name="Web">
        <Bindings>
          <Binding name="Endpoint1" endpointName="Endpoint1" />
        </Bindings>
      </Site>
    </Sites>-->
    <Endpoints>
      <InputEndpoint name="Endpoint1" protocol="http" port="80" />
    </Endpoints>
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
  </WebRole>
</ServiceDefinition>

Si ejecutamos de nuevo la aplicación, la misma arranca sin problemas :)

¡Saludos!

Bing Maps & Geolocation APIs

Para continuar con el tema de la georreferenciación, esta vez voy a mostraros cómo es posible posicionarnos en un mapa de Bing Maps, además de recuperar nuestra localización aproximada a través del API Geolocation (No disponible en todos los navegadores, aunque si en las últimas versiones de los más conocidos).

API Geolocation

Antes de posicionarnos en un mapa lo primero que debemos hacer es comprobar que el navegador soporta dicha API.

$(document).ready(function () {

   //Check if geolocation is available
   if (navigator.geolocation) {
       navigator.geolocation.getCurrentPosition(
           function (position) //success
           {
               GetMap(position);
           },
           function (error) //error
           {
               alert(error);
           }
       );
   }
   else
      GetMap(null);

});

Si navigator.geolocation no es undefined, podemos hacer la llamada a getCurrentPosition la cual acepta como parámetros dos funciones de callback: la primera si la llamada se ha realizado con éxito y la segunda en caso de error.

Bing Maps API

En el caso de Bing Maps, para poder utilizar la API es necesario registrarnos en Bing Maps Account Center con un Windows Live ID. Una vez logados, podremos acceder al menú donde seleccionaremos Create or view keys.

Damos de alta una nueva aplicación con el fin de generar una clave que posteriormente utilizaremos en la llamada.

var credentials = "App9aeMkRCpeXATFyZ8....cR9dqc6T4rIMG7XTnd";
var bingMap = null;

Por otro lado, necesitamos añadir un enlace al siguiente script para acceder a las funciones de la API.

<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=es-ES"></script>

Si nos fijamos en la referencia, vemos que al final aparece un parámetro llamado mkt que nos permite indicar la cultura del mapa. A día de hoy existen varias culturas disponibles.

Una vez obtenida la key y añadida la referencia al script de Bings Maps, ya podemos implementar la función GetMap, llamada desde el evento ready del documento.

function GetMap(position) {

    var lat = null;
    var long = null;

    if (position != null) {
        lat = position.coords.latitude;
        long = position.coords.longitude;
    }
    else{
        lat = 47.616023; //sample values (Seattle)
        long = -122.333565;
    }

    bingMap = new Microsoft.Maps.Map($("#mapDiv")[0],
                  { credentials: credentials,
                    center: new Microsoft.Maps.Location(lat, long),
                    mapTypeId: Microsoft.Maps.MapTypeId.road,
                    zoom: 16
                  });

   var center = bingMap.getCenter();
   var pin = new Microsoft.Maps.Pushpin(center, { text: '1' });
   Microsoft.Maps.Pushpin.prototype.title = null;
   pin.title = "Infobox Position";
   Microsoft.Maps.Pushpin.prototype.description = null;
   pin.description = "This is my position lat:" + lat + " long: " + long;

   //Add handler for the pushpin click event.
   Microsoft.Maps.Events.addHandler(pin, 'click', displayEventInfo);
   bingMap.entities.push(pin);
}

Si os dais cuenta, los pasos a seguir son básicamente los mismos que utilizábamos en la  API de Google Maps, a excepción del parámetro para las credenciales. En primer lugar, generamos el mapa, asignando como lienzo un div llamado mapDiv, pasamos la key como credencial, asignamos las coordenadas obtenidas como el centro del mapa, un tipo para el mismo y por último el valor del zoom.

Después creamos un objeto del tipo PushPin para añadir un punto de interés en el mapa con las coordenadas que utilizamos para establecer el centro y añadimos algunos valores como el titulo y la descripción, este último correspondiente al contenido del pop up.

Por último añadimos un handler al evento click, el cual llamará a la función displayEventInfo y adjuntamos el PushPin al mapa.

Para completar el ejemplo, creamos la función displayEventInfo que mostrará el pop up al hacer clic sobre el marker y agregamos los elementos HTML correspondientes al lienzo del mapa y el conjunto de divs que forman la ventana de información.

    function displayEventInfo(e) {
        if (e.targetType == "pushpin") {
            var pix = bingMap.tryLocationToPixel(e.target.getLocation(), Microsoft.Maps.PixelReference.control);
            $("#infoboxTitle").html(e.target.title);
            $("#infoboxDescription").html(e.target.description);

            var infobox = $("#infoBox");
            infobox.css({
                "top": (pix.y - 60) + "px",
                "left": (pix.x + 5) + "px",
                "visibility": "visible"
            });

            $("#mapDiv").append(infobox);
        }
    }   

</script>
<div id='mapDiv'>
</div>
<div id='infoBox'>
    <div id='infoboxText'>
        <b id='infoboxTitle'></b>
        <img id="imgClose" src="/Content/images/close_icon.gif" alt="close" />
        <a id='infoboxDescription'></a>
    </div>
</div>

Si accedemos a la aplicación desde Firefox vemos que aparece el siguiente mensaje de confirmación (No siempre deseamos que una aplicación sepa nuestras coordenadas):

Desde el momento en el compartimos nuestra ubicación, unos segundos después (a mi parecer creo que tarda bastante) aparece nuestro mapa con nuestra ubicación aproximada :D

Una sitio muy interesante para probar las funcionalidades que nos ofrece la API es Maps Interative SDK donde nos permite visualizar el resultado y recuperar el código asociado.

Más información

Adjunto el proyecto por si fuera de utilidad :)

¡Saludos!

Google Maps API v.3.0 y ASP.NET MVC 3 con Razor

 

Gracias a todas las APIs que nos facilita Google, podemos hacer uso prácticamente de todos los servicios que ofrece la compañía a día de hoy. En este post en concreto me gustaría hablaros de cómo utilizar el API de Google Maps en su versión 3.0 para generar mapas dinámicos con una enorme cantidad de funciones. Además, para rizar más el rizo utilizaré ASP.NET MVC 3 con Razor para crear una llamada síncrona y otra asíncrona con la ayuda de JQuery.

Referencias

Para utilizar el API de Google Maps son necesarias las siguientes referencias:

<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>

Nota: Cuando utilizas Google Maps siempre debe especificarse si vas a utilizar un sensor, por ejemplo un GPS, para determinar la ubicación del usuario (importante para los dispositivos móviles). En este caso estableceremos el valor del parámetro sensor a false ya que no entra este escenario dentro del post ;)

¿Por dónde empiezo?

Lo primero que debemos tener en cuenta es que podemos ubicar marcas de dos formas distintas: Indicando las coordenadas del sitio o recuperando las coordenadas a través de una dirección postal. Sin embargo, en el segundo caso tenemos una limitación por parte de la API, la cual sólo nos permite solicitar unas 10 direcciones por consulta, lo cual limita nuestro campo de acción. En este post utilizaremos coordenadas para poder agregar tantas marcas como sea necesario :D Para ello, he creado un objeto de prueba que almacenará todas las marcas y un repositorio,  también de prueba, que nos devolverá tres objetos dentro de una lista para la demo.

Objeto de prueba

namespace GoogleMapsASPNETMVC3.Models
{
    public class GoogleMarker
    {
        public string SiteName { get; set; }
        public double Latitude { get; set; }
        public double Longitude { get; set; }
        public string InfoWindow { get; set; }
    }
}

Repositorio de prueba


using System.Collections.Generic;

namespace GoogleMapsASPNETMVC3.Models
{
    public class MarkerRepository
    {

        public IList<GoogleMarker> GetMarkers()
        {
            var googleMarkers = new List<GoogleMarker>
                                    {
                                        new GoogleMarker
                                            {
                                                SiteName = "Jardines de Sabatini",
                                                Latitude = 40.421749,
                                                Longitude = -3.713994,
                                                InfoWindow = "InfoWindow de los Jardines de Sabatini"
                                            },
                                        new GoogleMarker
                                            {
                                                SiteName = "Campo del Moro",
                                                Latitude = 40.419658,
                                                Longitude = -3.718801,
                                                InfoWindow = "InfoWindow del Campo del Moro"
                                            },
                                        new GoogleMarker
                                            {
                                                SiteName = "Parque de la Cornisa",
                                                Latitude = 40.413254,
                                                Longitude = -3.716483,
                                                InfoWindow = "InfoWindow del Parque de la Cornisa"
                                            }
                                    };

            return googleMarkers;
        }
    }
}

Llamada síncrona

Para la llamada síncrona lo que vamos a hacer es solicitar la lista de marcas y pasarla a la vista para poder asociarla a la propiedad Model.

[HttpGet]
public ActionResult Sync()
{
     return View(_markerRepository.GetMarkers());
}

 Una vez en la vista agregaremos el siguiente código:

@model IEnumerable<GoogleMapsASPNETMVC3.Models.GoogleMarker>
@using System.Threading;
@using System.Globalization;
@{
    ViewBag.Title = "Sync";
    Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
}
<script language="javascript" type="text/javascript">
    $(document).ready(function () {
    var bounds = new google.maps.LatLngBounds();
    var options = {
        zoom : 14,
        mapTypeId: google.maps.MapTypeId.TERRAIN
    };
    var googleMap = new google.maps.Map($("#map")[0],options);
    var infoWindow = new google.maps.InfoWindow({ content: "Cargando..." });
    @foreach (var marker in Model)
    {
    <text>
        var point = new google.maps.LatLng(@marker.Latitude, @marker.Longitude);
        bounds.extend(point);

        var marker = new google.maps.Marker({
                position: point,
                map: googleMap,
                icon:'/Content/images/cloud_marker.png',
                html: '@marker.InfoWindow'
            });

        google.maps.event.addListener(marker, "click", function () {
                infoWindow.setContent(this.html);
                infoWindow.open(googleMap, this);
            });
    </text>
    }
    googleMap.fitBounds(bounds);
    });
</script>
<h2>
    Sync</h2>
<div id="map" style="width: 800px; height: 500px;"></div>

Lo primero que hacemos en la parte javascript es la inicialización de unas variables bases como son bounds donde vamos a insertar cada uno de los puntos de coordenadas, googleMap que asignará el espacio dedicado al mapa a un div con id “map” situado abajo del todo e infoWindow que nos servirá para asociar un pop up a cada marca del mapa.

Posteriormente utilizamos la @ para inicializar un foreach, ya que estamos utilizando el motor de vistas Razor, y recorremos cada una de las marcas ubicadas en el Model del tipo GoogleMarker. Como vamos a mezclar código javascript con variables de servidor, debemos hacer el uso de <text></text> para que no interprete todo el código dentro del foreach como código de servidor.

Los pasos que vamos a realizar en cada iteración es la creación de cada conjunto de coordenadas en el mapa a través de google.maps.LatLng para acto seguido usar el resultado como la posición de la marca en el objeto google.maps.Marker en el mapa asociado. En este último podemos asociar distintos tipos de opciones como por ejemplo el icono que queremos mostrar para dicha marca, sombreados, etcétera. En este caso he modificado la imagen por una nube :)

Además podemos asociar un evento a cada marca para mostrar el típico pop up que aparece cuando haces clic sobre una de ellas. Para ello utilizaremos google.maps.event.Listener donde crearemos la asociación con la clase InfoWindow instanciada al inicio del script.

Por otro lado, si os fijáis de nuevo en el código anterior, he cambiado la cultura a en-US. Esto es debido a que es necesario respetar el signo de separación de los decimales en las coordenadas ya que, de lo contrario, la latitud y longitud recuperadas de la propiedad Model se pintarían con comas y no se mostraría correctamente.

Como resultado obtendríamos la siguiente imagen :D

Llamada asíncrona

Para la llamada asíncrona el escenario cambia:

@{
    ViewBag.Title = "Async";
}
<script language="javascript" type="text/javascript">
    $(document).ready(function () {

        var bounds = new google.maps.LatLngBounds();
        var options = {
            zoom: 14,
            mapTypeId: google.maps.MapTypeId.TERRAIN
        };
        var googleMap = new google.maps.Map($("#map")[0], options);
        var infoWindow = new google.maps.InfoWindow({ content: "Cargando..." });

        $.ajax({
            type: "POST",
            url: "GetMarkersAsync",
            datatype: "json",
            success: function (data) {
                for (var i = 0; i < data.length; i++) {

                    var point = new google.maps.LatLng(data[i].Latitude, data[i].Longitude);

                    bounds.extend(point);

                    var marker = new google.maps.Marker({
                        position: point,
                        map: googleMap,
                        html: data[i].InfoWindow
                    });

                    google.maps.event.addListener(marker, "click", function () {
                        infoWindow.setContent(this.html);
                        infoWindow.open(googleMap, this);
                    });
                }
            }
        });

        googleMap.fitBounds(bounds);
    });
</script>
<h2>
    Async</h2>
<div id="map" style="width: 800px; height: 500px;">
</div>

En este segundo caso estamos utilizando JQuery para la llamada Ajax a una acción llamada GetMarkersAsync, la cual nos devolverá un listado de objetos en JSON con el mismo formato que nuestro objeto de prueba. Para ello, crearemos una acción HttpPost que retorne dicho listado basado en la clase GoogleMarker.

[HttpPost]
public ActionResult GetMarkersAsync()
{
    return Json(_markerRepository.GetMarkers());
}

En este caso el resultado es el mismo que en la llamada síncrona a excepción de los iconos utilizados en las marcas, ya que en el segundo ejemplo he omitido la propiedad icon para que utilice el icono por defecto de Google.

Adjunto el proyecto por si fuera de utilidad :D

Más información sobre la Google Map API 3.0

¡Saludos!

Microsoft Partner Network & Microsoft Platform Ready

A finales del año pasado se lanzó Microsoft Partner Network (MPN), el nuevo programa de partners con nuevos niveles y requisitos para empresas, con el objetivo de mejorar la calidad de los socios de Microsoft. Este nuevo concepto será el que releve a Microsoft Partner Program y para seguir aportando esta distinción a la empresa es necesario afrontar nuevos retos :D

Para conseguir la competencia de ISV en el nivel Silver se solicitará una aplicación certificada con uno de los siguientes tests:

  • Windows 7 Platform Ready
  • Windows Server 2008 R2 Platform Ready
  • Windows Azure Platform Ready
  • SQL Azure Platform Ready
  • Microsoft Surface Test

Para ello tenemos a nuestra disposición Microsoft Platform Ready que nos ayudará a realizar estos test en nuestros desarrollos de una manera rápida y sencilla, gracias a los recursos online que se nos ofrecen así como herramientas de testeo disponibles de forma gratuita para el partner. Por otro lado disponemos de un conjunto de utilidades que nos permitirán lanzar nuestra solución al mercado a través de los catálogos de Microsoft.

Date de alta en Microsoft Platform Ready con tu usuario y contraseña del programa de partners, da de alta todas tus aplicaciones, independientemente de la fase en la que estén (desarrollo, pruebas, producción, etc) y aprovecha los recursos de MPR (recursos de desarrollo, realiza test de certificación, material de marketing, etc).

 Si realizas algunos de los test, estos automáticamente volcaran en MPN y podrás asociarlo a la competencia para la que sean válidos.

 A parte de ISVs, hay otras competencias que permitirán la certificación de una aplicación para obtenerla en su nivel Silver. 

  • Application Integration  =  Windows Server 2008 R2 Platform Ready
  • Data Platform  =  SQL Server 2008 R2 Platform Ready
  • Business Intelligent  =  SQL Server 2008 R2 Platform Ready
  • Content Management   =  Sharepoint Server 2010 Platform Ready (proximamente disponible)
  • Unified Communication  =  Unified Communicatios Platform Ready ( aun no disponible)

Espero que esta información os sea de utilidad :)

¡Saludos!

Razor View Engine para ASP.NET MVC 3

Una de las novedades con mejor acogida de la nueva versión del frameworkd ASP.NET MVC 3 ha sido sin duda el nuevo motor de vistas llamado Razor. Si bien es cierto que con las primeras versiones y el motor de vistas aspx muchos programadores se echaban para atrás con ASP.NET MVC, con Razor podremos generar vistas de una forma más sencilla y fluida. En este post voy a comentar algunas de las diferencias y mejoras respecto al anterior view engine predeterminado.

En primer lugar, Razor pasa a ser el motor de vistas por defecto, aunque tenemos disponibles ambos al crear un nuevo proyecto o una nueva vista.

Accediendo a variables de servidor

Si recordáis, cuando trabajamos con el aspx view engine, para acceder al contenido de Model, ViewData, helpers, etcétera necesitábamos añadir las siguientes etiquetas: <%: %>

<p>Please enter your username and password. <%: Html.ActionLink("Register", "Register") %>
if you don't have an account.</p>

Con Razor simplemente utilizaremos @ al inicio y el view engine se encargará de determinar hasta dónde debe procesar.

<p>Please enter your username and password. @Html.ActionLink("Register", "Register") if
you don't have an account.</p>

Recorrer una lista

Para recorrer una lista es bastante similar a como lo haríamos con el motor aspx, con la diferencia del etiquetado para comenzar la sentencia foreach.

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Details", "Details", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
        <td>
            @item.FirstName
        </td>
        <td>
            @item.LastName
        </td>
        <td>
            @item.Address
        </td>
        <td>
            @item.E_Mail
        </td>
        <td>
            @item.Mobile
        </td>
    </tr>
}

Si nos fijamos en la llave de cierre del bucle, vemos que en Razor no es necesario etiquetar esa llave :D ya que se supone que pertenece a la llave de apertura abierta anteriormente, caso que no ocurría con aspx view engine:

<% foreach (var item in Model) { %>
    <tr>
        <td>
            <%: Html.ActionLink("Edit", "Edit", new { id=item.Id }) %> |
            <%: Html.ActionLink("Details", "Details", new { id=item.Id }) %> |
            <%: Html.ActionLink("Delete", "Delete", new { id=item.Id }) %>
        </td>
        <td>
            <%: item.FirstName %>
        </td>
        <td>
            <%: item.LastName %>
        </td>
        <td>
            <%: item.Address %>
        </td>
        <td>
            <%: item.E_Mail %>
        </td>
        <td>
            <%: item.Mobile %>
        </td>
    </tr>
<% } %>

Bloques de código

Por otro lado, para utilizar bloques de código bastará con utilizar la arroba y abrir llaves para delimitar el espacio abarcado por el bloque.

@{
    ViewBag.Title = "Loop";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Tag <text>

Otro escenario que se nos puede presentar es que dentro de un bloque de código necesitemos añadir código javascript o HTML que no requiera ser procesado por el motor.

<p>
    To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">
        http://asp.net/mvc</a>.
</p>
@if (DateTime.Now.Year == 2011)
{
    <text>while year is 2011 this should be shown.</text>
}

Para que nos hagamos una idea más clara, si no añadimos el tag <text>, algunas partes del código anterior sería interpretadas como palabras clave.

Archivo _ViewStart.cshtml

Para conseguir unas vistas más limpias y concisas, aparece un nuevo archivo llamado _ViewStart.cshtml.

 

El objetivo del mismo trata de recopilar código común a todas las vistas de la aplicación, simplificando el código de las mismas. Por defecto aparece definido en él el layout utilizado en las vistas, en caso de no ser especificado en las mismas.

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Sections 

Otra de las novedades dentro de Razor es la posibilidad de crear secciones.

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
</head>
<body>
    <div>
        <div id="header">
            <div id="title">
                <h1>
                    My MVC Application</h1>
            </div>
            <div id="logindisplay">
                @Html.Partial("_LogOnPartial")
            </div>
            <div id="menucontainer">
                <ul id="menu">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                </ul>
            </div>
        </div>
        <div id="main">
            @RenderBody()
            <div id="footer">
                @RenderSection("returngis");
            </div>
        </div>
    </div>
</body>
</html>

 Cuando definimos una sección en el layout, el objetivo principal es que la misma pueda ser modificada dinámicamente dependiendo de la vista que solicitemos de la siguiente manera.

@section returngis{
    <p>Este es un ejemplo para <a href="http://www.returngis.net">Returng(GiS);</a></p>
}

Si tenemos varias vistas dependiendo del anterior layout y no todas asignan un valor a la sección definida anteriormente, al acceder a alguna de las vistas sin esta definición saltaría la siguiente excepción:

Este error ocurre debido a que, por defecto, cuando se define una sección se da por hecho que la misma debe estar implementada en cualquier vista que utilice este layout. Para evitar esto, basta con modificar la definición de la sección agregando el parámetro false al final para convertir la sección de obligatorio a opcional.

@RenderSection("returngis", false)

Nuevos helpers

Por último, y no por ello menos importante :D , se añaden a nuestra lista de helpers 5 más, algunos de ellos imprescindibles:

  • Chart: A partir de la versión 3 no necesitaremos crear un chart from scratch como hacíamos en este antiguo post. Este helper renderiza exactamente las mismas características que en ASP.NET 4.
  • WebGrid: Un helper más que necesario el cual renderiza un grid con paginación y ordenación. Gracias a José M. Aguilar tenemos un tutorial de este helper explicando paso a paso cada una de las funcionalidades.
  • Crypto: Utilizado para crear password hash.
  • WebImage: Se utiliza para renderizar imágenes.
  • WebMail: Nos ayuda en el envío de emails.

Espero que sea de utilidad :D

¡Saludos!

Lleida dotnetclub: Descubriendo Windows Azure Platform

Para seguir con el hilo de los webcasts, el día 16 de Febrero tengo el placer de hablaros de la plataforma Windows Azure para todos aquellos que aún no sepáis exactamente de qué trata.

Desde Lleida dotnetclub hablaremos sobre qué es la plataforma, los servicios que ofrece y cómo podemos comenzar a utilizar la misma.

Podéis registraros a través del siguiente enlace.

¡Saludos!