3ENGINE

Programación y otros cachivaches

Archivo

Página 8/102

Tecnologia

Cómo crear un servicio Windows .NET autoinstalable


Cuando llega la hora de instalar un servicio Windows en .NET, en mucho sitios recomiendan instalar el servicio mediante una utilidad de la línea de comandos llamada installutil.exe

Comandos básicos para instalar y desinstalar un servicio con installutil.exe:

installutil.exe miservicio.exe
installutil.exe /u miservicio.exe

Una vez instalado podéis arrancar y parar el servicio mediante la utilidad net.exe:

net start miservicio.exe
net stop miservicio.exe

Crear un servicio Windows .NET autoinstalable

El problema de installutil.exe es que sólo la encontraras si instalas Visual Studio en el ordenador donde necesitas instalar el servicio. Una solución es crear un proyecto de InstallShield para crear un autoinstalable. Pero yo os cuento una manera mas sencilla de conseguir esto y ahorraros el sufrimiento de utilizar InstallShield 🙂

Antes de nada, el código incluye una directiva DEBUG que permite debugar el servicio como si se tratara de una aplicación de consola. Esto lo expliqué en Debug desde VS.NET de un servicio Windows.

Por último, recomiendo añadir un ProjectInstaller.cs a vuestro servicio tal como explica este enlace en el paso 5. Añadirlo nos permitirá entre otras cosas especificar el tipo de cuenta, el nombre y la descripción del servicio, el modo de arranque, etc.

Código de ejemplo:

using System.ServiceProcess;
using System.Configuration.Install;
using System.Reflection;
using System;
using System.Threading;

namespace MyService
{
    static class Program
    {
        /// 
        /// Punto de entrada principal para la aplicación.
        /// 
        static void Main(string[] args)
        {
            #if (!DEBUG)    
                if (System.Environment.UserInteractive)
                {
                    string parameter = string.Concat(args);
                    switch (parameter)
                    {
                        case "-i":
                        case "-install":
                            try
                            {
                                ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
                            }
                            catch
                            {
                            }
                            break;
                        case "-u":
                        case "-uninstall":
                            try
                            {
                                ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
                            }
                            catch
                            {
                            }
                            break;
                    }
                }
                else
                {
                    ServiceBase[] ServicesToRun;
                    ServicesToRun = new ServiceBase[] 
                    { 
                        new ServiceWatcher()
                    };
                    ServiceBase.Run(ServicesToRun);
                }

            #else
                // debug mode
                ServiceWatcher service = new ServiceWatcher();
                service.Init();
                Thread.Sleep(Timeout.Infinite);
            #endif
        }
    }
}

Básicamente la idea es que se puede tener un servicio autocontenido para instalar y desinstalar utilizando ManagedInstallerClass como se muestra el ejemplo.

De modo que si compilas el servicio en modo RELEASE para instalar el servicio:

miservicio.exe -install

y para desisntalarlo:

miservicio.exe -uninstall



Tecnologia

Decoradores Python con parámetros


Ya expliqué en una entrada anterior que es eso de los decordadores Python. Si todavia no lo has hecho te recomiendo que leas el artículo antes de seguir.

Decoradores con parámetros

Hace unos días tuve la necesidad de pasar parámetros a un decorador. Queria crear un decorador que retornara un valor por defecto si la función decorada retorna valor nulo. Entonces me di cuenta que mi anterior artículo no cubria esta aspecto.

Este es mi decorador:

def default_value(value):
    def _default_value(func):
        def box(*args, **kwargs):
            result = func(*args, **kwargs)
            if not result:
                return value
            return result
        return box
    return _default_value

Tenemos una funcion un tanto maleducada 😉 que sólo saluda a personas que se llamen David. La vamos a decorar para que retorne un saludo por defecto en caso que retorne nulo:

@default_value('hola desconocido')
def saluda(nombre):
    if nombre == 'david':
        return 'hola david'

Un test:

print saluda('david')
print saluda('antonio')

Y esta es la salida:

hola david
hola desconocido

Otra posible aplicación de los decoradores con parámetros, es si por ejemplo queremos devolver un valor por defecto sólo si la llamada a un función genera una excepción.

El código completo:

def default_try_value(value):
    def _default_try_value(func):
        def box(*args, **kwargs):
            try:
                result = func(*args, **kwargs)
            except:
                return value
            return result
        return box
    return _default_try_value
  
@default_try_value('error')
def divide_entre_2(numero):
    return numero / 2
    
print divide_entre_2(16)
print divide_entre_2('david')

Y esta es la salida:

8
error



Tecnologia

Reducir el tamaño de un GeoJSON con TopoJSON


TopoJSON es una extensión de GeoJSON que introduce un nuevo tipo Topology que contiene objetos GeoJSON que codifica topologías.

Las geometrías se definen a partir de segmentos de líneas compartidas llamadas arcos. Las reglas topológicas exigen que los polígonos compartan una frontera común sin huecos ni superposiciones entre ellos. TopoJSON aprovecha estas reglas de modo que si dos polígonos comparten frontera (arco) entonces TopoJSON representa una única vez la frontera eliminando así redundancias. Gracias a la eliminación de redundancias y a la cuantificación de las coordenadas un fichero TopoJSON puede llegar a ser un 80% más pequeño que su equivalente en GeoJSON.

Un ejemplo sencillo para entender cómo funciona

Dada una forma sencilla cerca de las coordenadas de latitud=0° y longitud=0° donde tenemos dos polígonos, identificados con el color rojo y verde respectivamente:

Reducir el tamaño de un GeoJSON con TopoJSON

Su representación en GeoJSON es la siguiente:

{
"type": "FeatureCollection",
"crs": {
"type": "name",
"properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84"}
},
"features": [
{
"type": "Feature",
"properties": {"ID": 1},
"geometry": {
"type": "Polygon", 
"coordinates": [[[0, 1],[0, 0],[1, 0],[1, 1],[0, 1]]]
}
},
{
"type": "Feature",
"properties": {"ID": 2},
"geometry": {
"type": "Polygon",
"coordinates": [[[1, 1],[1, 0],[2, 0],[2, 1],[1, 1]]]
}
}
]
}

En su transformación a TopoJSON se definen tres arcos identificados con el color verde, azul y rojo, donde el arco en color rojo es un arco compartido por los otros dos polígonos.

Reducir el tamaño de un GeoJSON con TopoJSON

Su representación en TopoJSON queda como sigue:

{
"type": "Topology",
"crs": {
"type": "name",
"properties": {"name": "urn:ogc:def:crs:OGC:1.3:CRS84"}
},
"transform": {
"scale": [1,1],
"translate": [0,0]
},
"arcs": [ 
          [[1,1],[0,-1]], 
  [[1,0],[-1,0],[0,1],[1,0]], 
          [[1,1],[1,0],[0,-1],[-1,0]]
],
"objects": {
"2cuadrados": {
"type": "GeometryCollection",
"geometries": [{
"arcs": [[0,1]],
"type": "Polygon",
"properties": {"ID": 1}
},
{
"arcs": [[2,-1]],
"type": "Polygon",
"properties": {"ID": 2}
}]
}
}
}

  • Línea 7: Para simplificar en el ejemplo no se ha aplicado una transformación lineal. El propósito de la transformación es cuantificar las posiciones para obtener una serialización más eficiente, mediante la representación de posiciones como números enteros.
  • Línea 11: Aquí se definen los arcos. En este caso hay 3 arcos. En cada uno de estos arcos la primera coordenada es cuantificada y el resto está definida respecto al punto anterior. El primer arco (rojo) va de la posición 1,1 a la 1,0. El segundo arco (verde) empieza en 1,0 y termina en 1,1 y el tercer arco (azul) empieza en 1,1 y termina en 1,0
  • Línea 19: Aquí se definen las geometrías. En este caso hay 2 geometrías. Cada una una de ellas se componen de varios arcos. La primera geometria se compone del arco [0] (rojo) y del arco [1] (verde). La segunda geometría se componen del arco [2] (azul) y del arco [-1] (rojo). El [-1] indica que la secuencia de las coordenadas en el arco debe ser invertida antes de la unión. Para evitar la ambigüedad con cero, se utiliza complemento a uno, de modo que [-1] representa el arco invertido [0] y así sucesivamente.

Exportar a TopoJSON

A continuación enumero algunas herramientas que permiten exportar a TopoJSON.

Topojson

Topojson es una herramienta de línea de comandos que permite los siguente formatos de entrada:

  • .json GeoJSON or TopoJSON
  • .shp ESRI shapefile
  • .csv comma-separated values (CSV)
  • .tsv tab-separated values (TSV)

Para instalar la herramienta necesitarás Node.js. Instala mediante NPM:

npm install -g topojson

Ejemplo básico que convierte el fichero GeoJSON input.json a un fichero TopoJSON output.json:

topojson -o output.json input.json

Mapshaper

Mapshaper es una herramienta online, que ademas podemos instalar y manejar desde la línea de comandos (mas información en una entrada anterior) que permite simplificar geometrías y exportar, entre otros formatos a TopoJSON.

Ejemplo básico que convierte el fichero GeoJSON input.json a un fichero TopoJSON output.json:

mapshaper input.json -o output.json format=topojson

Distillery

Distillery es una sencilla herramienta online que admite GeoJSON como formato de entrada, opcionalmente simplificar geometrías y exportar a TopoJSON.

Reducir el tamaño de un GeoJSON con TopoJSON

Geojson.io

Geojson.io es otra herramienta online avanzada para la edición GeoJSON con multitud de opciones, entre ellas la de exportar, entre otros formatos, a TopoJSON

Reducir el tamaño de un GeoJSON con TopoJSON

Utilizar TopoJSON en Leafleft

En un artículo anterior expliqué cómo crear un mapa con GeoJSON y Leafleft. Leafleft no soporta TopoJSON y será necesario transformar TopoJSON a GeoJSON. Para hacer esto, descarga la libreria topojson desde aquí y referenciala. Es posible que necesites referenciar también a jQuery:

<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<script type="text/javascript" src="topojson.min.js"></script> 

Despues utiliza/adapta el siguiente código que descarga y transforma a GeoJSON:

// Transform topojson to geojson. 
// Copyright (c) 2013 Ryan Clark
L.TopoJSON = L.GeoJSON.extend({  
addData: function(jsonData) {    
if (jsonData.type === "Topology") {
for (key in jsonData.objects) {
geojson_ = topojson.feature(jsonData, jsonData.objects[key]);
L.GeoJSON.prototype.addData.call(this, geojson_);
}
}    
else {
L.GeoJSON.prototype.addData.call(this, jsonData);
}
}  
});

var geojson = new L.TopoJSON(undefined, {
style: style,
onEachFeature: onEachFeature
});

$.getJSON('mytopojson.topojson').done(addTopoData);

function addTopoData(topoData){  
geojson.addData(topoData);
geojson.addTo(map);
}




Tecnologia

Cómo reducir el tamaño de un GeoJSON


Uno de los problemas del webmapping es que necesita descargar la información espacial de un servidor. Y esto ha de ser lo más rápido posible. GeoJSON aun siendo un formato sencillo, dependiendo del número de coordenadas que contenga, puede ser muy pesado y por lo tanto el mapa puede tardar mucho tiempo en cargar.

Para la demo he aprovechado el GeoJSON de ejemplo que usé en un artículo anterior donde explico cómo crear un mapa con Leafleft y GeoJSON. Este GeoJSON ocupa de origen la friolera de 1.6 MB :-). Veamos cómo reducir el tamaño de un GeoJSON para mejorar la velocidad de nuestra aplicación webmapping:

Opción1: reducir el tamaño de un GeoJSON con QGIS (Quantum GIS)

En primer lugar cargamos el GeoJSON

Cómo reducir el tamaño de un GeoJSON

Después vamos a Vectorial > Herramientas de geometría > Simplificar geometrías. Seleccionamos la capa a simplificar, la tolerancia de simplificación, activamos y especificamos donde guardar el resultado y finalmente clic en aceptar:

Cómo reducir el tamaño de un GeoJSON

Como resultado de la simplificación QGIS genera un shapefile (.shp). Ahora sólo nos falta exportar el shapefile a formato GeoJSON, especificamos donde guardar el resultado y clic en aceptar. Opcionalmente podemos seleccionar otro sistema de coordenadas diferente al de origen (SRC) y la precisión de las coordenadas. Esto última puede ayudar a reducir aún más el GeoJSON.

Cómo reducir el tamaño de un GeoJSON

En mi caso, mientras que el GeoJSON original ocupaba 1.6MB el GeoJSON simplificado ahora ocupa 192KB.

Cómo reducir el tamaño de un GeoJSON

Opción2: reducir el tamaño de un GeoJSON con mapshaper

La principal ventaja de esta herramienta es que al ser online no necesita instalación aunque también podemos instalar la aplicación y manejar la herramienta desde la línea de comandos.

Acepta Shapefile, GeoJSON y TopoJSON como formatos de entrada y salida y tres métodos de simplificación de geometría. Seleccionamos el porcentaje de simplificación y exportamos al formato deseado.

Cómo reducir el tamaño de un GeoJSON

En mi caso, mientras que el GeoJSON original ocupaba 1.6MB el GeoJSON simplificado ahora ocupa 171KB.

mapshaper3

Opción3: reducir el tamaño de un GeoJSON con mapshaper desde línea de comandos

Una vez instalado node.js puedes instalar la última versión de mapshaper con npm:

npm install -g mapshaper

Existe una introducción a la herramienta por línea de comandos que muestra todo lo que puede hacer. En nuestro caso para simplificar el GeoJSON escribimos lo siguiente:

mapshaper demo.json -simplify 10% -o resultado.json