Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /var/www/vhosts/cadetill.com/domains/cadetill.com/public_html/wp-content/plugins/qtranslate-x/qtranslate_frontend.php on line 497

Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /var/www/vhosts/cadetill.com/domains/cadetill.com/public_html/wp-content/themes/suffusion/functions/media.php on line 666

Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /var/www/vhosts/cadetill.com/domains/cadetill.com/public_html/wp-content/themes/suffusion/functions/media.php on line 671

Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /var/www/vhosts/cadetill.com/domains/cadetill.com/public_html/wp-content/themes/suffusion/functions/media.php on line 684

Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /var/www/vhosts/cadetill.com/domains/cadetill.com/public_html/wp-content/themes/suffusion/functions/media.php on line 689

Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /var/www/vhosts/cadetill.com/domains/cadetill.com/public_html/wp-content/themes/suffusion/functions/media.php on line 694
delphi – Página 6 – El blog de cadetill
abr 162012
 

Buenas,

Hoy vengo a presentaros una nueva versión (la 0.1.1 Pre-Alpha) de los componentes GMLib. Las novedades de esta nueva versión son las siguientes:

  • Un nuevo componente, el TGMPolyline, para la creación de polilineas (una polilinea es un conjunto de puntos unidos entre sí mediante líneas).
  • Corrección de algún bug encontrado.
  • Se le ha añadido la propiedad AutoOpen a la clase TInfoWindow.
  • Se ha añadido el evento OnPositionChange a la clase TInfoWindow.
  • Muchos cambios en el JavaScript para hacer las funciones más genéricas.
  • Se ha añadido el método Clear a la clase base TGMLinkedComponent.
  • Se ha añadido el método Assign a la clase base TGMLinkedComponent.
  • Se ha añadido el método Clear en la clase base TLinkedComponents.
  • Se ha cambiado las referencias a la API de Google Maps al nuevo dominio.
  • Cuando se cambia la lat/lng de un marcador, se cambia automáticamente en el mapa.
  • Se ha añadido el método Assign en la clase TGMSize.

Podéis encontrar más explicaciones en la página de los componentes.

Espero que os sea de utilidad

Nos leemos

 

mar 202012
 

Buenas,

A raíz de los diferentes artículos hechos sobre el uso de la API de Google Maps desde aplicaciones Delphi, se me ocurrió el realizar una serie de componentes que encapsularan dicho funcionamiento y hacer más fácil la vida al programador.

De momento os presento los tres primeros componentes:

  • TGMMaps: componente que diseña el mapa plano, sin objetos, sólo con lo necesario para visualizar una zona y desplazarte por la misma (zoom, scroll, ……). También es el que gestiona la comunicación con el mapa a nivel de eventos y/o cambio de cualquier tipo de propiedad/objeto del mapa. A su vez, mantiene un TList con todo objeto que se linca a él y que represente algún tipo de objeto en el mapa (marcadores, ventanas de información, lineas, polilíneas, rectángulos,……).
  • TGMMarker: componente que mantiene una relación de marcadores (clase TMarker) mediante una TCollection (TMarkers).
  • TGMInfoWindow: componente que mantiene una relación de ventanas de información o balloons (clase TInfoWindow) mediante una TCollection (TInfoWindows).

El próximo componente será el TGMPolyline, para gestionar las polilíneas.

He creado una página específica de estos componente donde se explica el funcionamiento, sus propiedades y métodos y desde donde podréis descargar los fuentes y demos.

Espero que os sean de utilidad

Nos leemos

nov 102011
 

Buenas,

Haciendo unos componentes (que a la que estén algo visibles publicaré), me encontré con la necesidad de tener que hacer algo como tienen los TStatusBar con sus paneles o los TDBGrid con sus columnas, es decir, quería tener una propiedad en que pudiera crear objetos del mismo tipo.

Pregunté en mi foro preferido, delphiaccess al respecto y el buen amigo Héctor me abrió los ojos. La solución a mis problemas era la clase TCollection. Marteens explica muy bien su uso en este trick, así que aquí sólo voy a dar 2 pinceladas.

La clase TCollection

Una característica importante de la clase es que deriva de TPersistent, con lo que no está oblidaga a tener un propietario o owner. Por esta razón, cuando derivamos de TCollection tenemos la obligación de redefinir el método GetOwner que en TPersistent devuelve un simple nil y no está redefinido en TCollection.

Una alternativa al problema anterior es derivar de TOwnedCollection, la cual ya se encarga de redefinir ese método (de hecho es lo único que hace).

A parte de este detalle, no se necesita hacer nada más en la clase que derivemos de TCollection (si no lo necesitamos, claro).

Personalizando TCollection

Los objetos que «guarda» TCollection son o tienen que derivar de TCollectionItem. A su vez, TCollection tiene una propiedad Items para devolvernos un elemento de los que contiene. No obstante, no tiene propiedad por defecto y, claro está, Items devuelve un TCollectionItem.

Estos dos detalles hacen o obligan al programador a tener que estar referenciando la propiedad Items cada vez y a tener que usar la conversión de tipos contínuamente, por lo que es aconsejable ocultar la antigua propiedad Items. Sería algo así:

  TMyCollection = class(TCollection)
  private
    function GetItems(I: Integer): TMarker;
    procedure SetItems(I: Integer; const Value: TMarker);
  public
    property Items[I: Integer]: TMarker read GetItems write SetItems; default;
  end;

implementation

function TMyCollection.GetItems(I: Integer): TMarker;
begin
  Result := TMyCollectionItem(inherited Items[I]);
end;

procedure TMyCollection.SetItems(I: Integer; const Value: TMarker);
begin
  inherited SetItem(I, Value);
end;

Con este sencillo código facilitaremos en mucho el trabajo al programador que use nuestro componente.

La clase TCollectionItem

Cómo he comentado más arriba, los objetos que almacena TCollection tienen que ser o derivar de la clase TCollectionItem. En ésta le pondremos las propiedades, métodos y eventos que necesite nuestro objeto y que podrán ser retocados en tiempo de diseño (en el caso de las propiedades published).

Cuando se hereda de esta clase, suele ser buena costumbre añadir a la nueva clase alguna propiedad de tipo string (Name, Caption, Title,… decirla cómo queráis) que nos sirva para ayudar al inspector de objetos a mostrar un nombre coherente o con sentido a simple vista. Para ello tendremos que redefinir el método GetDisplayName haciendo algo así:

  TMyCollectionItem = class(TCollectionItem)
  private
    FTitle: string;
  protected
    function GetDisplayName: String; override;
  published
    property Title: string read FTitle write FTitle;
  end;

implementation

function TMyCollectionItem.GetDisplayName: String;
begin
  if Length(Title) > 0 then
  begin
    if Length(Title) > 15 then Result := Copy(Title, 0, 12) + '...'
    else Result := Title
  end
  else
    Result := inherited GetDisplayName;
end;

Y la última cosa obligada a la que estamos es redefinir el método Assign para que Delphi sepa qué hacer en caso de asignación entre dos objetos del mismo tipo. Algo así:

  procedure TMyCollectionItem.Assign(Source: TPersistent);
begin
  if Source is TMyCollectionItem then
  begin
    Title := TMyCollectionItem(Source).Title;
    //..... el resto de propiedades
  end
  else
    inherited Assign(Source);
end;

Bueno, espero que os sirva esta breve explicación de la clase TCollection y TCollectionItem

Nos leemos

 

nov 022011
 

Buenas,

Haciendo unas aplicaciones que usan el componente TWebBrowser, me surgió la necesidad de interactuar con las páginas cargadas en él, en concreto el manejo de los formularios. Consultando a Google llegué a delphi.about.com donde encontré una serie de artículos que me ayudaron a solucionar mis necesidades.

Gracias a estos artículos he creado una clase (TWebControl) que engloba buena parte de lo expuesto en esos artículos.

Entre las funciones interesantes que tiene esta clase tenemos:

  • procedure WebLinks: se encarga de carturar todas las etiquetas «a href» que contenga la página y las pone en la propiedad Links.
  • procedure WebFormNames: se encarga de capturar todos los formularios que contenga la página y los almacena en la propiedad Forms.
  • procedure WebFormFields: dado el nombre de un formulario, se encarga de rellenar la propiedad Fields con los campos del mismo.
  • procedure WebFormSetFieldValue: dado un formulario y un campo del mismo, establece un valor a dicho campo.
  • procedure WebFormSubmit: dado un formulario, lo acepta.
  • function WebFormFieldValue: dado un formulario y un campo del mismo, devuelve el valor que contiene.
  • function WebHTMLCode: devuelve el código HTML de la página web.
  • function WebContent: devuelve el contenido de la página web.
  • procedure WebSaveAsHTML: dado un nombre de archivo, guarda la página web.
  • procedure WebLoadHTML: dado un código HTML, lo carga directamente en el TWebBrowser.
  • procedure WebAddHTML: dado un código HTML, lo añade a la página existente.
  • procedure WebPrintWithoutDialog: imprime la página web sin mostrar ningún dialogo.
  • procedure WebPrintWithDialog: lanza el diálogo de impresión.
  • procedure WebPrintPreview: realiza una previsualización de la impresión de la página web.
  • procedure WebPrintPageSetup: realiza una llamada al diálogo del setup.

La forma de funcionar de la clase es sencilla. Basta con crear un objeto de la misma pasándole en el constructor el objeto TWebBrowser que contenga la página a controlar.

Como siempre, podéis descargaros el programa demo y la unit con la clase desde aquí.

Nos leemos

oct 192010
 

Buenas,

Vimos en un mensaje anterior cómo configurar visualmente nuestro mapa de Google Maps. Antes de ver el StreetView quería comentar una cosa al respecto. En la documentación del API podemos observar la existencia de un método llamado setOptions que, como parámetro, recibe un objeto de tipo MapOptions. Por el nombre del método y el parámetro recibido podríamos pensar que no sería necesaria la creación de todo el mapa si queremos cambiar el comportamiento inicial del mapa. Pues bien, he realizado la prueba y no termina de funcionar como debe, así que daremos por válido lo que decía antes de que se debe de establecer todo en la creación.

Y ahora, el StreetView

Bien, dicho ésto, vamos a ver el StreetView.

Cualquier mapa de Google Maps contiene la imagen de un hombrecito en la zona del zoom (ver imagen 1 al pie del mensaje) que arrastrándolo y soltándolo donde queramos, nos abre el StreetView. Fijaros que, en el momento de arrastrarlo, donde existe StreetView se colorea en azul (ver imagen 2 al pie del mensaje).

Este hombrecito es, como todo aspecto visual de un mapa de Google Maps, configurable en la creación del mapa. Estas propiedades las obvié en el mensaje anterior expresamente para poderlas explicar ahora. Por supuesto, pertenecen al objeto MapOptions.

  • streetViewControl: booleano para mostrar o no el hombrecito.
  • streetView: propiedad de tipo StreetViewPanorama que representa la panorámica StreetView que se mostrará al soltar el hombrecillo. Si no se define ninguna, se generará una predeterminada y se mostrará en el div del mapa. Os recomiendo una lectura de esta clase dado que aquí sólo veremos el constructor de la misma, el cual recibe dos parámetros, container que es dónde se mostrará (un div) y opts de tipo StreetViewPanoramaOptionsdel que veremos ahora sus propiedades:
    • addressControl: booleano para mostrar o ocultar el cartel con la dirección.
    • addressControlOptions: propiedad de tipo StreetViewAddressControlOptionscon las opciones de visualización del control de dirección.
      • position: propiedad de tipo ControlPosition para especificar dónde aparecerá el control. Por defecto TOP_LEFT.
      • style: string con la especificación CSS a aplicar al control de posición. Por ejemplo, {backgroundColor: ‘red’}.
    • enableCloseButton: booleano para mostrar o ocultar el botón de cierre de la ventana StreetView.
    • linksControl: booleano para mostrar o ocultar el control de desplazamiento por las calles.
    • navigationControl: booleano para mostrar o ocultar el control de navegación.
    • navigationControlOptions: propiedad de tipo NavigationControlOptionscon las opciones de visualización del control de navegación.
      • position: propiedad de tipo ControlPosition para especificar dónde aparecerá el control. Por defecto TOP_LEFT.
      • style: propiedad de tipo NavigationControlStyle para especificar el tipo de navegador.
    • position: propiedad de tipo LatLng donde se centrará el mapa.
    • pov: propiedad de tipo StreetViewPovpara especificar la orientación de la cámara (según ángulo, inclinación y zoom).
      • heading: numérico que expresa el ángulo en grados respecto al norte absoluto siendo el norte 0º, el este 90º, el sur 180º y el oeste 270.
      • pitch: numérico que expresa la inclinación de la cámara en grados respecto al vehículo de Street View y oscila entre 90º (arriba) y -90º (abajo).
      • zoom: numérico para indicar el nivel del zoom siendo el más lejano el 0.

En la imagen 3 podéis ver a qué se refiere cada una de las propiedades.

Desde Delphi

Vista la teoría, llega el momento de ponerlo en práctica.

Cuando creamos un mapa, tenemos dos opciones, especificar una panorámica o dejar que el objeto mapa cree una predeterminada. Con la primera opción, podremos controlar todo el aspecto visual del StreeView. En cambio, si dejamos que se cree la predeterminada, mediante los métodos del StreetViewPanorama sólo podremos controlar la longitud/latitud (método setPosition) y la orientación de la cámara (método setPov).

Una vez creado el mapa, sea de la forma que sea, podemos acceder a la panorámica del mismo gracias al método getStreetView de la clase Map. Así mismo, podríamos establecer una panorámica nueva mediante setStreetView.

En nuestro ejemplo, escogeremos la opción de personalizar el StreetView en la creación del mapa y mostrarlo en un div diferente al mapa, así podremos ver las diferentes alternativas que tenemos. Para ello, lo primero es modificar la pantalla para poder acceder a las opciones del StreetView (ver imagen 4 al pie del mensaje). Hay que tener presente que, si las coordenadas pasadas no tienen una referencia válida para StreetView, éste no aparecerá.

Lo primero que hay que hacer es adaptar nuestra función JavaScript para poder recibir la configuración del StreetView. Para ello añadiremos 8 nuevos parámetros. También se tiene que crear el objeto StreetViewPanorama con sus opciones. Y puestos a modificar, para no repetir código, crearemos funciones para la obtención de las diferentes constantes.

  function GetPosition(aPos) {
    switch (aPos) {
      case "BOTTOM": return google.maps.ControlPosition.BOTTOM; break;
      case "BOTTOM_LEFT": return google.maps.ControlPosition.BOTTOM_LEFT; break;
      case "BOTTOM_RIGHT": return google.maps.ControlPosition.BOTTOM_RIGHT; break;
      case "LEFT": return google.maps.ControlPosition.LEFT; break;
      case "RIGHT": return google.maps.ControlPosition.RIGHT; break;
      case "TOP": return google.maps.ControlPosition.TOP; break;
      case "TOP_LEFT": return google.maps.ControlPosition.TOP_LEFT; break;
      default: return google.maps.ControlPosition.TOP_RIGHT;
    }
  }

  function GetNavigationControl(aNav) {
    switch (aNav) {
      case "ANDROID": return google.maps.NavigationControlStyle.ANDROID; break;
      case "SMALL": return google.maps.NavigationControlStyle.SMALL; break;
      case "ZOOM_PAN": return google.maps.NavigationControlStyle.ZOOM_PAN; break;
      default: return google.maps.NavigationControlStyle.DEFAULT;
    }
  }

  function MenuStyle(aStyleMenu) {
    switch (aStyleMenu) {
      case "DROPDOWN_MENU": return google.maps.MapTypeControlStyle.DROPDOWN_MENU; break;
      case "HORIZONTAL_BAR": return google.maps.MapTypeControlStyle.HORIZONTAL_BAR; break;
      default: return google.maps.MapTypeControlStyle.DEFAULT;
    }
  }

  function MapType(aTMap) {
    switch (aTMap) {
      case "HYBRID": return google.maps.MapTypeId.HYBRID; break;
      case "ROADMAP": return google.maps.MapTypeId.ROADMAP; break;
      case "SATELLITE": return google.maps.MapTypeId.SATELLITE; break;
      default: return google.maps.MapTypeId.TERRAIN;
    }
  }

  function DoWeb(Lat,Lon,TMap,aZoom,ZClick,MoveMap,Keyb,Wheel,HideAll,ShowTM,
                 PosTM,StyleTM,ShowNav,PosNav,StyleNav,ShowScale,PosScale,
                 SV,DirecSV,PosDirecSV,CloseSV,LinksSV,NavSV,PosNavSV,StyleNavSV) {
    if (map != null) map.setStreetView(null);
    panorama = null;
    map = null;

    aTMap = MapType(TMap);
    aPosTM = GetPosition(PosTM);
    aStyleTM = MenuStyle(StyleTM);
    aPosNav = GetPosition(PosNav);
    aStyleNav = GetNavigationControl(StyleNav);
    aPosScale = GetPosition(PosScale);
    aPosDirecSV = GetPosition(PosDirecSV);
    aPosNavSV = GetPosition(PosNavSV);
    aStyleNavSV = GetNavigationControl(StyleNavSV);

    var latlng = new google.maps.LatLng(Lat,Lon); 

    var panoOpts = {
      addressControl: DirecSV,
      addressControlOptions: {position: aPosDirecSV},
      enableCloseButton: CloseSV,
      linksControl: LinksSV,
      navigationControl: NavSV,
      navigationControlOptions: {position: aPosNavSV, style:aStyleNavSV},
      position: latlng
    }

    panorama =
        new google.maps.StreetViewPanorama(document.getElementById("streetview"),
        panoOpts);

    var myOptions = {
      center: latlng,
      mapTypeId: aTMap,
      zoom: aZoom, 

      disableDoubleClickZoom: ZClick,
      draggable: MoveMap,
      keyboardShortcuts: Keyb,
      scrollwheel: Wheel, 

      mapTypeControl: ShowTM,
      mapTypeControlOptions: {position: aPosTM, style: aStyleTM},
      navigationControl: ShowNav,
      navigationControlOptions: {position: aPosNav, style: aStyleNav},
      scaleControl: ShowScale,
      scaleControlOptions: {position: aPosScale},

      streetViewControl: true,
      streetView: panorama
    };
    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  }

A nivel de código Delphi, lo único que tendremos que hacer es modificar la llamada a la función JavaScript pasándole los nuevos parámetros.

Si quisiéramos que la panorámica de StreetView se viera en el mismo div que el mapa, bastará con que cambiemos la línea de creación del panorama y pasarle de parámetro el div del mapa

panorama =
    new google.maps.StreetViewPanorama(document.getElementById("map_canvas"),
    panoOpts);

No obstante, con las pruebas que he realizado, sólo funciona correctamente la primera vez que se crea el mapa con la panorámica de StreetView personalizada. Si alguien hace pruebas y ve cómo arreglarlo, estaría agradecido que me lo comentara.

Más en próximos artículos.

 

Como siempre, podéis descargaros el programa demo desde aquí.

Nos leemos

oct 092010
 

Buenas,

Una vez visto cómo mostrar un mapa de Google Maps, cómo centrarlo y cómo poner marcas, ahora toca el momento de personalizarlo.

En la versión 3 del API de Google Maps, hay que tener en cuenta que no se permite añadir o eliminar controles de forma dinámica, éstos deben de establecerse en la creación del mapa.

El objeto MapOptions

Este objeto será el encargado de dar la forma inicial a nuestro mapa. Para ver todas sus propiedades, te aconsejo una visita a la API de Google Maps ya que aquí sólo veremos los más interesantes (o al menos desde mi punto de vista).

Obligatorias
  • center: propiedad que determina dónde se centrará el mapa al mostrarlo. Es obligatoria y es de tipo LatLng.
  • mapTypeId: propiedad de tipo MapTypeId obligatoria y que es cómo se mostrará inicialmente el mapa.
  • zoom: entero obligatorio con el zoom aplicado al mapa.
Visuales
  • disableDefaultUI: booleano para mostrar o no todos los controles del mapa. Por defecto están habilitados.
  • mapTypeControl: booleano para mostrar o no el tipo de mapa. Por defecto habilitado.
  • mapTypeControlOptions: propiedad del tipo MapTypeControlOptions para configuración del control del tipo de mapa cuando éste se muestra.
    • mapTypeIds: array con los tipos de mapa a mostrar. Ver MapTypeId.
    • position: propiedad de tipo ControlPosition para especificar dónde se mostrará el control. Por defecto TOP_RIGHT. Las constantes posibles son:
      • BOTTOM
      • BOTTOM_LEFT
      • BOTTOM_RIGHT
      • LEFT
      • RIGHT
      • TOP
      • TOP_LEFT
      • TOP_RIGHT
    • style: propiedad de tipo MapTypeControlStyle para determinar el estilo del control. Los posibles valores son:
      • DEFAULT
      • DROPDOWN_MENU
      • HORIZONTAL_BAR
  • navigationControl: booleano para mostrar o no el control de navegación.
  • navigationControlOptions: propiedad de tipo NavigationControlOptions con las propiedades del control de navegación.
    • position: propiedad de tipo ControlPosition.
    • style: propiedad de tipo NavigationControlStyle para especificar el tipo de navegador. Las constantes posibles son:
      • ANDROID
      • DEFAULT
      • SMALL
      • ZOOM_PAN
  • scaleControl: booleano para mostrar o no el control de escala.
  • scaleControlOptions: propiedad de tipo ScaleControlOptions con las opciones del control de escala.
No visuales
  • disableDoubleClickZoom: booleano para des/habilitar el zoom con el clic de ratón.
  • draggable: booleano para des/habilitar el poder mover el mapa. Por defecto se puede.
  • keyboardShortcuts: booleano para des/habilitar el control del mapa con el teclado. Por defecto habilitado.
  • scrollwheel: booleano para des/habilitar el control de la rueda del ratón. Habilitado por defecto.

Aplicando lo visto a Delphi

Una vez vistas las configuraciones posibles del mapa, nos queda ver cómo hacerlo desde Delphi.

Basándonos en los ejemplos anteriores, crearemos una constante (MAPA_CODE) que contendrá el código html y JavaScript necesario. En éste, destacar la creación de una nueva función JavaScript que será llamada desde Delphi y será la encargada de generar el mapa con la configuración que hayamos especificado en el programa. La función es esta:

  function DoWeb(Lat,Lon,TMap,aZoom,ZClick,MoveMap,Keyb,Wheel,ShowTM,
                 PosTM,StyleTM,ShowNav,PosNav,StyleNav,ShowScale,PosScale) {
    switch (TMap) {
      case "HYBRID": aTMap = google.maps.MapTypeId.HYBRID; break;
      case "ROADMAP": aTMap = google.maps.MapTypeId.ROADMAP; break;
      case "SATELLITE": aTMap = google.maps.MapTypeId.SATELLITE; break;
      default: aTMap = google.maps.MapTypeId.TERRAIN;
    } 

    switch (PosTM) {
      case "BOTTOM": aPosTM = google.maps.ControlPosition.BOTTOM; break;
      case "BOTTOM_LEFT": aPosTM = google.maps.ControlPosition.BOTTOM_LEFT; break;
      case "BOTTOM_RIGHT": aPosTM = google.maps.ControlPosition.BOTTOM_RIGHT; break;
      case "LEFT": aPosTM = google.maps.ControlPosition.LEFT; break;
      case "RIGHT": aPosTM = google.maps.ControlPosition.RIGHT; break;
      case "TOP": aPosTM = google.maps.ControlPosition.TOP; break;
      case "TOP_LEFT": aPosTM = google.maps.ControlPosition.TOP_LEFT; break;
      default: aPosTM = google.maps.ControlPosition.TOP_RIGHT;
    } 

    switch (StyleTM) {
      case "DROPDOWN_MENU": aStyleTM = google.maps.MapTypeControlStyle.DROPDOWN_MENU; break;
      case "HORIZONTAL_BAR": aStyleTM = google.maps.MapTypeControlStyle.HORIZONTAL_BAR; break;
      default: aStyleTM = google.maps.MapTypeControlStyle.DEFAULT;
    } 

    switch (PosNav) {
      case "BOTTOM": aPosNav = google.maps.ControlPosition.BOTTOM; break;
      case "BOTTOM_LEFT": aPosNav = google.maps.ControlPosition.BOTTOM_LEFT; break;
      case "BOTTOM_RIGHT": aPosNav = google.maps.ControlPosition.BOTTOM_RIGHT; break;
      case "LEFT": aPosNav = google.maps.ControlPosition.LEFT; break;
      case "RIGHT": aPosNav = google.maps.ControlPosition.RIGHT; break;
      case "TOP": aPosNav = google.maps.ControlPosition.TOP; break;
      case "TOP_LEFT": aPosNav = google.maps.ControlPosition.TOP_LEFT; break;
      default: aPosNav = google.maps.ControlPosition.TOP_RIGHT;
    } 

    switch (StyleNav) {
      case "ANDROID": aStyleNav = google.maps.NavigationControlStyle.ANDROID; break;
      case "SMALL": aStyleNav = google.maps.NavigationControlStyle.SMALL; break;
      case "ZOOM_PAN": aStyleNav = google.maps.NavigationControlStyle.ZOOM_PAN; break;
      default: aStyleNav = google.maps.NavigationControlStyle.DEFAULT;
    } 

    switch (PosScale) {
      case "BOTTOM": aPosScale = google.maps.ControlPosition.BOTTOM; break;
      case "BOTTOM_LEFT": aPosScale = google.maps.ControlPosition.BOTTOM_LEFT; break;
      case "BOTTOM_RIGHT": aPosScale = google.maps.ControlPosition.BOTTOM_RIGHT; break;
      case "LEFT": aPosScale = google.maps.ControlPosition.LEFT; break;
      case "RIGHT": aPosScale = google.maps.ControlPosition.RIGHT; break;
      case "TOP": aPosScale = google.maps.ControlPosition.TOP; break;
      case "TOP_LEFT": aPosScale = google.maps.ControlPosition.TOP_LEFT; break;
      default: aPosScale = google.maps.ControlPosition.TOP_RIGHT;
    } 

    var latlng = new google.maps.LatLng(Lat,Lon);
    var myOptions = {
      center: latlng,
      mapTypeId: aTMap,
      zoom: aZoom, 

      disableDoubleClickZoom: ZClick,
      draggable: MoveMap,
      keyboardShortcuts: Keyb,
      scrollwheel: Wheel, 

      mapTypeControl: ShowTM,
      mapTypeControlOptions: {position: aPosTM, style: aStyleTM},
      navigationControl: ShowNav,
      navigationControlOptions: {position: aPosNav, style: aStyleNav},
      scaleControl: ShowScale,
      scaleControlOptions: {position: aPosScale} 

    };
    map = null;
    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  }
Las constantes posibles son:

Como podemos ver, la función recibe tantos parámetros como posibles configuraciones tengamos. Lo primero que hace es coger el valor de las constantes necesarias según los parámetros recibidos (los 6 switch). Luego creamos el objeto que contendrá la longitud y latitud y el que contendrá todas las opciones del mapa. Para terminar, liberamos el objeto map por si existía y lo volvemos a crear con las características nuevas.

A nivel de código Delphi, pues queda hacer la llamada cómo ya hemos visto en anterioridad:

const
  StrParams = '%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s';
var
  Doc2: IHTMLDocument2;
  Win2: IHTMLWindow2;
  Params: String;
begin
  Doc2 := wbWeb.Document as IHTMLDocument2;
  Win2 := Doc2.parentWindow;

  Params := Format(StrParams, [eLong.Text,
                               eLat.Text,
                               QuotedStr(cbTypMap.Text),
                               IntToStr(tbZoom.Position),
                               LowerCase(BoolToStr(not chZoomClick.Checked, True)),
                               LowerCase(BoolToStr(chDrag.Checked, True)),
                               LowerCase(BoolToStr(chTeclado.Checked, True)),
                               LowerCase(BoolToStr(chWheel.Checked, True)),
                               LowerCase(BoolToStr(chShowTypeMap.Checked, True)),
                               QuotedStr(cbTMPosition.Text),
                               QuotedStr(cbTMStyle.Text),
                               LowerCase(BoolToStr(chNavigation.Checked, True)),
                               QuotedStr(cbNPosition.Text),
                               QuotedStr(cbNStyle.Text),
                               LowerCase(BoolToStr(chScale.Checked, True)),
                               QuotedStr(cbSPosition.Text)
                               ]);

  Win2.execScript('DoWeb(' + Params + ')', 'JavaScript');
end;

Aquí os dejo un ejemplo de la pantalla.

También podéis descargaros los fuentes y el binario del programa desde aquí.

Otro día más.

Nos leemos

oct 052010
 

Buenas,

Vimos en un mensaje anterior cómo conseguir posicionar el mapa en una determinada longitud y latitud. Ahora vamos a ver cómo poner marcas en estas longitudes y latitudes.

Para poner una marca en el mapa no es necesario tenerlo centrado dado que ésta es relativa al mapa, no al hecho del centrado. Así pues, siguiendo el ejemplo anterior, haremos que cuando se carguen las diferentes pestañas .

La clase Marker

Para crear una marca, lo que tendremos que hacer es crear un objeto de la clase Marker, así que os recomiendo que le deis un vistazo a sus métodos y eventos. Para nuestro propósito, lo que realmente nos interesa es el constructor, el cual recibe un objeto de tipo MarkerOptions con el que podremos configurar el aspecto de nuestra marca.

Este objeto tiene las siguientes propiedades:

  • clickable: booleano que nos indicará si podemos o no hacer clic en la marca.
  • cursor: string que contiene el cursor de ratón a mostrar.
  • draggable: booleano que permitirá que la marca pueda desplazarse.
  • flat: booleano que mostrará o no la sombra de la marca.
  • icon: string o MarkerImage con el icono a mostrar para la marca.
  • map: mapa asociado donde se mostrará la marca.
  • position:LatLng donde se mostrará la marca.
  • shadow: string o MarkerImage con la imagen de sombra.
  • shape: MarkerShape que indica la región dónde se podrá hacer clic o desplazar.
  • title: string con el título (hint para que nos entendamos los Delphinianos).
  • visible: booleano para hacer o no visible la marca.
  • zIndex: numérico que indicará el orden (superposición si se diera el caso) en que se mostrarán las marcas.

Cambios en el JavaScript

A nivel de JavaScript tendremos que realizar algunos cambios. Primero crearemos una función que nos genere la marca.

  function MakeMarker(Lat, Lng, titulo) {
    var latlng = new google.maps.LatLng(Lat,Lng);
    var aMarker = new google.maps.Marker({
        position: latlng,
        map: map,
        title: titulo
        });
    markerArray[markerArray.length] = aMarker;
  }

De esta función destacar el array markerArray que contendrá todas las marcas puestas en el mapa. Este array nos servirá para, en búsquedas posteriores, poder borrar las marcas antes de mostrar las nuevas. Esta variable se ha definido global como la variabla map.

Otra función que añadiremos es la de borrar las marcas

  function DeleteMarkers() {
    for (i = 0; i < markerArray.length; i++) {
      markerArray[i].setMap(null);
    }
    markerArray = [];
  }

Ahora sólo queda controlar esto desde Delphi.

Cambios en Delphi

Por lo que respecta a Delphi, crearemos un par de funciones

    procedure PonerMarca(const Long, Lat, Titulo: string);
    procedure BorrarMarcas;

Estas dos funciones se llamarán desde el evento AfterGetValues de la clase TGeoCode.

Puedes descargarte un ejemplo aquí (incluye ejecutable).

Nos leemos

sep 302010
 

Buenas,

En los eventos de ratón de un TStatusBar solemos encontrar las coordenadas X, Y en la que se produce la acción, pero no así el panel, el cual puede ser interesante saber si queremos realizar una acción determinada según el panel (mostrar un hint dependiendo del panel, un menú contextual,…). La siguiente función nos devolverá dicho panel del TStatusBar.

function GetStatusBarPanelXY(StatusBar: TStatusBar; X, Y: Integer) : Integer;
var
  i: Integer;
  R: TRect;
begin
  Result := -1;

  // Buscamos panel a panel hasta encontrar en cual está XY
  with StatusBar do
    for i := 0 to Panels.Count - 1 do
    begin
      // Obtenemos las dimensiones del panel
      SendMessage(Handle, WM_USER + 10, i, Integer(@R));
      if PtInRect(R, Point(x,y)) then
      begin
        Result := i;
        Break;
      end;
    end;
end;

Los eventos del ratón que tienen coordenadas y con los que nos servirá la función son los siguientes:

  • OnMouseActivate
  • OnMouseDown
  • OnMouseMove
  • OnMouseUp
  • OnEndDrag
  • OnEndDock
  • OnDragOver
  • OnDragDrop
  • OnContextPopup (no tiene X e Y, pero tiene un TPoint que para el caso es lo mismo)

Espero que os sirva.

Nos leemos

sep 302010
 

Buenas,

Vimos en un mensaje anterior la clase TIniFiles, la cual nos permite el acceso fácil a los ficheros de configuración. Cuando en una aplicación se hace mucho uso de este tipo de ficheros, en hecho de tener que estar todo el rato escribiendo las mismas líneas de código para crear y destruir un objeto de la clase TIniFile puede llegar a ser cansino. Lo que haremos ahora es crearnos nuestra propia clase que manipule un objeto de tipo TIniFile.

Los métodos de la nueva clase pueden llegar a ser los mismos que los que tiene TIniFile, no obstante podemos poner los que queramos o necesitemos.

Vamos a llamar a esta nueva clase TFicherosIni y la idea es crear una variable global de la unit de este tipo y controlar su creación y destrucción en la inicialización (initialization) y finalización (finalization) de la misma respectivamente.

unit FicherosIni;

interface

type
  TFicherosIni = class
  ....
  end;

var
  Ini: TFicherosIni;

implementation
.....

initialization
  Ini := TFicherosIni.Create;

finalization
  FreeAndNil(Ini);

end.

Con esto, sólo necesitaremos poner en la cláusula uses del formulario que queramos usarlo la llamada a esta unit y usar directamente el objeto Ini.

Tengo la costumbre de que los archivos ini se llamen igual que la aplicación que los usa, así pues, la clase TFicherosIni tiene una propiedad llamada NameExe la cual, al asignarse, automáticamente se asocia el fichero ini. No obstante, dado que no siempre es así, también dispone del método SetFileIni para especificar uno en concreto.

Aquí podéis descargaros la unit así como una demo de su uso.

Espero que os sea de utilidad.

Nos leemos

sep 292010
 

Buenas,

En muchas aplicaciones es habitual tener algún tipo de parámetro configurable que no puede estar almacenado en una base de datos o que simplemente el aplicativo no dispone de base de datos. Además, si no disponemos de un instalador que nos permita añadir información al registro de Windows, tenemos la opción de usar archivos de configuración o archivos ini.

Un archivo de configuración se divide en secciones, y estas secciones pueden tener una o más claves con sus respectivos valores. La estructura sería esta:

[sección 1]
clave11=valor11
clave12=valor12

[sección 2]
clave21=valor21

....

Como podemos ver, el nombre de las secciones va entre corchetes ([]) y los pares clave/valor van con un igual (=) y sin espacios de por medio.

Se pueden tener tantas secciones como se quiera/necesite, así como tantos pares de claves/valor por sección.

La gracia de este tipo archivos es que se puede acceder a una determinada clave de una sección cualquiera de forma muy sencilla y, Delphi nos proporciona una unidad (IniFiles) para la gestión de los mismos.

Veamos un ejemplo de cómo acceder:

var
  Ini: TIniFile;
begin
  Ini := TIniFile.Create('c:\MiFicheroIni.ini');
  try
    Edit1.Text := Ini.ReadString('Seccion 1', 'clave1', '');
  finally
    FreeAndNil(Ini);
  end;

El código es simple, declaramos y creamos un objeto de tipo TIniFile. Al constructor de la clase se le pasa como parámetro el fichero de configuración al que queremos acceder. Luego, para leer, usamos el método ReadString al que se le pasa cómo parámetro la sección, la clave y un valor por defecto por si no existe dicha sección/clave.

En el ejemplo hemos visto cómo leer un string, pero se puede leer o escribir diferentes tipos de datos.

Métodos de lectura

A nivel de lectura tenemos los siguientes métodos:

    function ReadString(const Section, Ident, Default: string): string;
    function ReadInteger(const Section, Ident: string; Default: Longint): Longint;
    function ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
    function ReadBinaryStream(const Section, Name: string; Value: TStream): Integer;
    function ReadDate(const Section, Name: string; Default: TDateTime): TDateTime;
    function ReadDateTime(const Section, Name: string; Default: TDateTime): TDateTime;
    function ReadFloat(const Section, Name: string; Default: Double): Double;
    function ReadTime(const Section, Name: string; Default: TDateTime): TDateTime;
    procedure ReadSection(const Section: string; Strings: TStrings);
    procedure ReadSections(Strings: TStrings);
    procedure ReadSections(const Section: string; Strings: TStrings);
    procedure ReadSubSections(const Section: string; Strings: TStrings; Recurse: Boolean = False);
    procedure ReadSectionValues(const Section: string; Strings: TStrings);

Las funciones está claro lo que hacen, así que no las comentaré. Por lo que respecta a los procedimientos…

  • ReadSection: devuelve en el TStrings las claves de la sección pasada por parámetro.
  • ReadSections: devuelve en el TStrings las secciones que contiene el fichero de configuración.
  • ReadSectionValues: devuelve en el TStrings la combinación de todas las clave/valor de la sección especificada.

Métodos de escritura

A nivel de escritura de ficheros de configuración tenemos los siguientes métodos:

    procedure WriteString(const Section, Ident, Value: String);
    procedure WriteInteger(const Section, Ident: string; Value: Longint);
    procedure WriteBool(const Section, Ident: string; Value: Boolean);
    procedure WriteBinaryStream(const Section, Name: string; Value: TStream);
    procedure WriteDate(const Section, Name: string; Value: TDateTime);
    procedure WriteDateTime(const Section, Name: string; Value: TDateTime);
    procedure WriteFloat(const Section, Name: string; Value: Double);
    procedure WriteTime(const Section, Name: string; Value: TDateTime);

Como está bastante claro lo que hacen, no hace falta comentar nada.

Otros métodos de la clase

Además de los de lectura y escritura, tenemos otros métodos para interactuar con los ficheros de configuración y son:

    function SectionExists(const Section: string): Boolean;
    procedure EraseSection(const Section: string);
    procedure DeleteKey(const Section, Ident: String);
    procedure UpdateFile;
    function ValueExists(const Section, Ident: string): Boolean;

Veamos qué hacen:

  • EraseSection: borra la sección pasada por parámetro.
  • DeleteKey: borra la clave (Ident) de la sección especificada (Section).
  • UpdateFile: fuerza la escritura a disco del fichero de configuración.
  • ValueExists: indica si una determinada clave (Ident) existe en una determinada sección (Section).

Para terminar, recomendaros la lectura de esta entrada del blog.

Nos leemos