Calcolare le distanze geodetiche fra comuni d’Italia in php

coordinate.pngIn un precedente articolo avevo pubblicato il codice di una funzione in ANSI C per il calcolo delle distanze sulla superficie terrestre. Poiché il C e il PHP sono due linguaggi dalla sintassi assai simile, ho deciso di fare la traduzione del codice. Inoltre, per dare un senso pratico al lavoro, ho trovato sul web l’archivio completo dei comuni italiani con le relative coordinate geografiche e su questo ho costruito un programmino PHP interattivo che permette di selezionare due comuni ed ottenere la relativa distanza geodetica, ovvero la lunghezza dell’arco massimo che unisce i due punti rappresentati dalle coordinate dei due comuni.
Va sottolineato che, come spiegato in questo ottimo articolo, per distanze molto piccole la formula che utilizzo fornisce un errore piuttosto grosso, quindi i risultati vanno presi come puramente indicativi.

Ecco il codice della funzione:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function disgeod($latA, $lonA, $latB, $lonB){
  define ("R","6371");
  /* Converte i gradi in radianti */
  $lat_alfa = M_PI * $latA / 180;
  $lat_beta = M_PI * $latB / 180;
  $lon_alfa = M_PI * $lonA / 180;
  $lon_beta = M_PI * $lonB / 180;
  /* Calcola l'angolo compreso fi */
  $fi = abs($lon_alfa - $lon_beta);
  /* Calcola il terzo lato del triangolo sferico */
  $p = acos(sin($lat_beta) * sin($lat_alfa)
   + cos($lat_beta) * cos($lat_alfa) * cos($fi));
  /* Calcola la distanza sulla superficie terrestre R = ˜6371 Km */
  $d = $p * R;
  return($d);
}

E questa invece è la funzione che converte i gradi sessagesimali in decimali:

1
2
3
4
5
6
7
8
function deg2dec($d=0,$m=0,$s=0,$direction)
{
  $decimal=($d+($m/60)+($s/3600));
  //South latitudes and West longitudes need to return a negative result
  if (($direction=="S") or ($direction=="W"))
          { $decimal=$decimal*(-1);}
  return $decimal;
}

Per evitare di dover caricare le due select con tutti gli 8104 comuni, cosa che avrebbe rallentato il caricamento della pagina in modo improponibile, ho utilizzato uno script AJAX che provvede a popolare le select dinamicamente con i soli nomi dei comuni di una certa provincia preselezionata.

Potete provare il programma su questo link (Il programma è stato testato con Firefox 2 e con Internet Explorer 7)

Download

L’intero pacchetto (circa 1Mb) comprende anche l’archivio in formato sql, txt, xls e csv.

Conclusioni

Lo sviluppo di questo programmino è stato un esercizio interessante, che mi ha permesso di approfondire anche l’utilizzo e il funzionamento degli script AJAX sui quali credo che tornerò in futuro.

Riferimenti ed approfondimenti:

Bigdump: una utility per importare dump voluminosi in MySQL

screenshot di bigdumpHo trovato questa utility di Alexey Ozerov dopo un vano tentativo di fare l’upload di un voluminoso dump di dati su un server MySQL.
I problemi che si verificano in questi casi sono dovuti ad alcune impostazioni del PHP nel file di configurazione php.ini: upload_max_filesize, memory_limit, post_max_size, o alle impostazioni di timeout per gli script PHP: max_execution_time (default = 30s).
Inoltre, sui server di produzione, e su gran parte dei server in hosting gestiti con Plesk, non si hanno i diritti per cambiare le impostazioni dei file di configurazione per ovvie ragioni di sicurezza. Se ciò non bastasse, questi server solitamente sono impostati in modalità safe, quindi non si ha nemmeno la possibilità di modificare a runtime i valori dei parametri preimpostati. Per completare l’opera frequentemente non si ha nemmeno a disposizione una shell per i comandi MySQL.

Per fortuna quelli di phpMyAdmin lo sanno e quando ci si imbatte in un errore dovuto a queste limitazioni, l’applicazione stessa suggerisce l’utilizzo di Bigdump (ma non garantiscono l’utilizzo, perché lo script è di terze parti).
Questo piccolo ma prodigioso script PHP esegue piccole porzioni del dump, ripartendo in sessioni successive fino al termine della query. In questo modo lo script rimane sempre nei limiti di memoria imposti, anche quelli più restrittivi.

screenshot di bigdumpL’utilizzo è semplicissimo: per prima cosa bisogna aprirlo con un editor ed impostare i parametri di connessione al database, poi si può fare l’upload sul server web e modificargli i permessi (chmod 777 su Linux). Una volta lanciato, la prima schermata richiede di selezionare il file .sql che viene caricato sul server spezzato in chunk di 2Mb, per evitare i limiti di upload_max_filesize.
Si può anche usare FTP per trasferire il dump sul server, in questo caso bisogna metterlo nella stessa directory dove risiede Bigdump. A questo punto nella schermata compare il nome del file caricato ed accanto un link con “start import”, basta fare un click ed aspettare pazientemente!

Per darvi un’idea del tempo che serve, posso dirvi che per un dump di 10 tabelle InnoDB con 12000 records per un totale di circa 2Mb ci sono voluti circa 10′. Con tabelle MyISAM è molto più veloce, ad esempio con un file di 8Mb contenente una tabella con 33500 records ha impiegato pochi secondi.

Questo script utilizza AJAX e javascript per mostrare lo stato di avanzamento dell’operazione in modo asincrono e senza ricaricare la pagina nel browser, dunque javascript deve essere abilitato. Da sottolineare anche che il progetto è in fase beta, dunque non è dichiarato stabile, comunque nelle prove che ho fatto non ho avuto nessun problema. Infine una raccomandazione, è molto importante cancellare Bigdump dal server una volta terminata l’importazione perché si può facilmente intuire la pericolosità dello stesso in mani diverse dal gestore del db!

Download:

Bigdump.zip

Conclusioni:

Senza Bigdump avrei dovuto scrivermi uno script simile a Bigdump, ma perché perdere tempo? Secondo me è utilissimo!

Riferimenti ed approfondimenti:

PHP: un semplice datagrid per applicazioni web

Esempio di datagridE’ piuttosto frequente, quando si sviluppano applicazioni web, di dover presentare i dati di una query in un datagrid, ovvero in una tabella HTML. In tutti questi casi, può essere comodo avere già pronta una classe che formatta i dati nella tabella e scrive per noi il codice HTML. Questa classe che ho scritto, pur essendo molto semplice e spartana, presenta alcune caratteristiche interessanti:

  • Il codice generato è XHTML valido
  • La presentazione è gestita da fogli stile CSS, quindi facilmente personalizzabile
  • E’ possibile, tramite il passaggio di parametri, creare una colonna aggiuntiva di link per azioni tipo “modifica”, o “dettaglio”
  • E’ possibile visualizzare o nascondere la colonna contenente la chiave primaria
  • Aspetto gradevole e alternanza di colorazione delle singole righe per una più facile lettura dei dati

Attenzione: il codice di questa classe non è più disponibile per obsolescenza

Riferimenti ed approfondimenti:

PHP: Come creare un array di etichette da una tabella dati

Può capitare di dover generare report di etichette, per esempio codici a barre, da una tabella normalizzata contenente i valori delle quantità in un campo. A noi, però, serve poter stampare tante etichette quanto è il valore ‘quantità’ di ogni singolo pezzo.
Ora, sebbene normalmente sia sufficiente, per ogni record, ciclare tante volte per il valore del campo quantità, in certi casi potrebbe essere comodo avere una funzione che fa questo per noi. In particolar modo quando è necessario utilizzare parecchie volte questa procedura.

Immaginiamo di avere una tabella come la seguente, risultato magari di una SELECT

rifnomecodiceqta
a001chiodichd0013
a001vitivt0012
a002rivettirvt0014
a002puntept0011

Simuliamo di aver immagazzinato la query in un array multidimensionale associativo, attraverso l’istruzione mysql_fetch_array:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$data=array
(
    0 => array
        (
            'rif' => 'a001',
            'nome' => 'chiodi',
            'codice' => 'chd001',
            'qta' => 3
        ),
    1 => array
        (
            'rif' => 'a001',
            'nome' => 'viti',
            'codice' => 'vt001',
            'qta' => 2
        ),
    2 => array
        (
            'rif' => 'a002',
            'nome' => 'rivetti',
            'codice' => 'rvt001',
            'qta' => 4
        ),
    3 => array
        (
            'rif' => 'a002',
            'nome' => 'punte',
            'codice' => 'pt001',
            'qta' => 1
        )
);

Lo sviluppo della funzione prevede, come dati di input: l’array dei dati, il nome del campo che contiene le quantità, e il nome dei due campi in base ai quali ordinare l’array.
Ovviamente è possibile modificare questo codice per permettere l’ordinamento con un maggior numero di chiavi. In output la funzione fornisce l’array delle etichette già ordinato.

Vediamo il codice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function labelmaker($table,$qty,
                    $order1,$order2,
                    $typeorder1=SORT_ASC,
                    $typeorder2=SORT_ASC)
{
  $newdata=array(); // array di appoggio
  $max=count($table);
  for($i=0;$i<$max;$i++){
    foreach ($table[$i] as $k => $value) {
      if($k==$qty && $value>1)
      {
        $q=$table[$i][$k];
        $table[$i][$k]=1;
        for($j=1;$j<$q;$j++)
        {
          array_push($newdata, $table[$i]);
        }
      }
    }
  }
//Unione dei due array
  $res = array_merge($table, $newdata);
// Ottiene un array di colonne per le chiavi
  foreach ($res as $key => $row) {
      $arrorder1[$key] = $row[$order1];
      $arrorder2[$key] = $row[$order2];
  }
// Ordina l'array in base alle chiavi
  array_multisort($arrorder1, $typeorder1,
        $arrorder2, $typeorder2, $res);
    return $res;
}

Note: la funzione accetta l’array in input passato come valore, e restituisce un nuovo array. Questo comporta inevitabilmente un consumo di memoria, in considerazione del fatto che
l’algoritmo prevede anche l’utilizzo di un array di ‘appoggio’, è bene utilizzare questa procedura per una mole di dati ragionevole!

Infine, la funzione viene fornita ‘as is’, senza controllo di errori, né prove efficaci in produzione.

Ecco come viene richiamata:

$lblarr= labelmaker($data,"qta",
         "rif","nome",SORT_ASC,SORT_ASC);

Mostriamo il risultato:

echo '<pre>';
print_r($lblarr);
echo '</pre>';

E questo è il risultato:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
Array
(
    [0] => Array
        (
            [rif] => a001
            [nome] => chiodi
            [codice] => chd001
            [qta] => 1
        )
    [1] => Array
        (
            [rif] => a001
            [nome] => chiodi
            [codice] => chd001
            [qta] => 1
        )
    [2] => Array
        (
            [rif] => a001
            [nome] => chiodi
            [codice] => chd001
            [qta] => 1
        )
    [3] => Array
        (
            [rif] => a001
            [nome] => viti
            [codice] => vt001
            [qta] => 1
        )
    [4] => Array
        (
            [rif] => a001
            [nome] => viti
            [codice] => vt001
            [qta] => 1
        )
    [5] => Array
        (
            [rif] => a002
            [nome] => punte
            [codice] => pt001
            [qta] => 1
        )
    [6] => Array
        (
            [rif] => a002
            [nome] => rivetti
            [codice] => rvt001
            [qta] => 1
        )
    [7] => Array
        (
            [rif] => a002
            [nome] => rivetti
            [codice] => rvt001
            [qta] => 1
        )
    [8] => Array
        (
            [rif] => a002
            [nome] => rivetti
            [codice] => rvt001
            [qta] => 1
        )
    [9] => Array
        (
            [rif] => a002
            [nome] => rivetti
            [codice] => rvt001
            [qta] => 1
        )
)

Conclusioni:

Questa funzione può costituire il prototipo per tutti i casi in
cui si ha la necessità di produrre stampe di etichette, da assegnare a singoli pezzi,
in modo direttamente proporzionale ad un certo quantitativo definito in tabella.

Riferimenti ed approfondimenti:

P4A framework: come stampare un pdf

P4A: toolbar con pulsante pdfNella documentazione del framework PHP P4A della Crealabs, non ci sono informazioni troppo dettagliate su come
produrre report in formato pdf. Cercando nei thread del forum, ci sono diversi frammenti ed indizi, che
aiutano parecchio, ma si perde un po’ di tempo a metterli insieme per produrre il codice necessario a
generare il report come si desidera. In questo articolo mostrerò la soluzione che ho scelto mettendo insieme i vari indizi, e applicandola alla base application “Products Catalogue” distribuita sul sito della Crealabs come applicazione di esempio.

Step 1: scelta ed installazione delle librerie pdf

In questo thread del forum, viene suggerito di usare le librerie FPDF o R&OS.
Ho scelto R&OS semplicemente perché in passato avevo avuto qualche difficoltà con FPDF, probabilmente per mia colpa. Credo che siano in sostanza equvalenti.
Una volta scaricato il pacchetto, è necessario creare nella root della applicazione (nel nostro caso: localhost/p4a/applications/products_catalogue/) una cartella che si deve chiamare
libraries. All’interno di questa cartella vengono decompresse le librerie (nel nostro caso i due files: class.pdf.php e class.ezpdf.php) e la cartella fonts contenente i file per la generazione dei font.

Step 2: Aggiunta di un pulsante nella toolbar

A questo punto bisogna creare un pulsante per la generazione del report in pdf. Personalmente ho scelto di crearlo nella toolbar, ma evidentemente si può crearlo dove si vuole.
All’interno del file products.php sarà sufficiente aggiungere questo codice PHP:

1
2
3
4
5
6
7
8
9
10
// Toolbar
$toolbar = &amp; $this->build("p4a_standard_toolbar",
                              "toolbar");
$toolbar->setMask($this);
$toolbar->addSeparator($position = "left");
// Aggiungo un pulsante alla toolbar per la stampa su pdf
$toolbar->addButton('stampapdf','pdf');
// Intercetto onClick per eseguire stampa_documento()
$this->intercept($toolbar->buttons->stampapdf,
                 'onClick','stampa_documento');

Notate che, per comodità, ho creato una variabile $toolbar contenente l’oggetto toolbar.
Il secondo parametro 'pdf' passato al metodo addButton crea automaticamente un img link all’icona pdf.png.
Questa icona deve essere presente nella cartella di default per il set di icone di P4A: localhost/p4a/icons/default/32/

In alternativa, si può impostare a NULL questo parametro per lasciare solo la scritta impostata ('stampapdf').

L’ultima riga di codice intercetta l’evento onClick sul pulsante ed esegue il metodo stampa_documento

Step 3: Aggiunta del metodo stampa_documento

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function stampa_documento()  
// funzione per la stampa su pdf
  {
  require('class.ezpdf.php');
  $pdf =&amp; new Cezpdf('a4','portrait');
  $pdf->selectFont($_SERVER['DOCUMENT_ROOT'].
                   P4A_APPLICATION_PATH.
                   '/libraries/fonts/Helvetica');
  $db =&amp; p4a_db::singleton();
  $res = $db->getAll("SELECT model,
   brands.description AS brand,
   categories.description AS category, selling_price
   FROM products
   LEFT JOIN brands
   ON brands.brand_id = products.brand_id
   LEFT JOIN categories
   ON categories.category_id = products.category_id");
  $pdf->ezTable($res,'','Products',array('fontSize'=>12));
  $type = 'application/pdf';
  $filename = 'report.pdf';
  header("Pragma: public");
  header("Cache-Control: must-revalidate, post-check=0,
         pre-check=0");
  header("Cache-Control: private", false);
  header("Content-type: $type");
  header("Content-Disposition: attachment;
          filename=\"$filename\"");
  $pdfcode = $pdf->output(1); // flusso dati senza header
  header("Content-Length: " . strlen($pdfcode));
  echo $pdfcode;
  die();
  }

Note: nella variabile $res viene memorizzato un’array multidimensionale associativo che è il risultato della query. Come esempio ho preparato una query di tipo JOIN perché nella maggior parte dei casi, è proprio questo il tipo utilizzato.

Il path per la scelta dei font deve essere assoluto, per averlo in termini relativi è possibile utilizzare la variabile $_SERVER['DOCUMENT_ROOT'] propria del PHP e la costatnte P4A_APPLICATION_PATH propria del framework P4A.

Per forzare il browser ad aprire il report pdf in una nuova finestra, ho cambiato l’header del documento generato, specificando che il contenuto è un attachment. Altrimenti, il contenuto viene riconosciuto come mime-type 'application/pdf' e (a seconda dei browser utilizzati) viene aperto nella stessa finestra.

Infine il nuovo processo viene terminato attraverso l’istruzione die();

Download:

Il sorgente comprende anche le librerie R&OS e l’SQL per creare il database con alcuni record d’esempio per il report in pdf. Il report generato è visualizzabile qui.

Conclusioni:

Il framework P4A è uno strumento davvero ben fatto, gli autori hanno fatto un gran lavoro, adesso sta a noi utilizzatori, attraverso l’interscambio di informazioni, generare la documentazione necessaria al suo sviluppo.
A tal proposito è mia intenzione produrre altra documentazione su altri aspetti di comune interesse agli utilizzatori di questo ottimo framework.

Riferimenti ed approfondimenti:


Valerio Maglietta ha scritto:

Definire ottima questa imbeccata di Mario Spada e’ il minimo…
L’unica aggiunta che mi sento di consigliare è quello di preprocessare il contenuto della stringa da stampare tramite iconv.
Ovvero per noi Italiani:

$res = iconv(‘UTF-8’, ‘CP1252’, $res);

prima dare in pasto $res a ezTable come nell’esempio precedente

In questa maniera caratteri accentati, simbolo euro e simili, non ci daranno problemi.
Nel mio caso (so linux Ubuntu 7.10) la codifica iniziale è UTF-8 ma potrebbe essere differente (ISO-8859-1, ISO-8859-16).
CP1252 è la codifica Windows Western European (sembra che R&OS sia stato sviluppato proprio sotto win…).

Grazie 1k Mario!

Un framework PHP facile, moderno e italiano: P4A

Esempio di applicazione p4aL’utilizzo di un framework fornisce ai programmatori PHP che hanno scelto le applicazioni web come via di sviluppo, un comodo strumento per la costruzione delle maschere, le connessioni ai database, l’implementazione di menù e pulsantiere e tutti gli altri aspetti dell’applicazione che sono accessori indispensabili di qualsiasi programma. In questo modo lo sviluppatore si può concentrare maggiormente sulla logica dell’applicazione, lasciando al framework e ai suoi widget il compito della presentazione e della interazione con i dati.
Per chi ha scelto come linguaggio di sviluppo il PHP, non c’è che l’imbarazzo della scelta. Ma quali sono i criteri per fare una scelta corretta?

Secondo la mia opinione, principalmente due: il tipo di prodotto che si deve realizzare e il tempo di apprendimento per padroneggiare il framework. Altri criteri importanti sono la documentazione e la portabilità. Infatti se il framework richiede, per la propria installazione, requisiti difficilmente presenti sulla maggior parte dei provider, sarà poi difficile mettere in piedi un prodotto competitivo. Avendo l’esigenza di sviluppare un’applicazione per la gestione della produzione di una piccola impresa, la nostra scelta è caduta su un framework tutto italiano sviluppato da Andrea Giardina e Fabrizio Balliano della Crealabs denominato: P4A (PHP for Applications).

Quali sono i principali vantaggi di P4A?

  • Semplicità dell’installazione e portabilità
  • Apprendimento molto rapido
  • Persistenza dello stato durante l’esecuzione dell’applicazione
  • Presenza di utili widget per menù, navigazione del database, creazione di form e tabelle
  • Presenza di un buon numero di applicazioni base ed esempi
  • Semplicità del codice sorgente
  • Struttura moderna, orientata agli oggetti
  • l’HTML generato presenta un buon grado di accessibilità
  • Supporto ajax
  • Possibilità di interagire con gli autori in lingua italiana (chi fa questo mestiere dovrebbe conoscere bene l’inglese… ma esprimersi in madre lingua è sempre un’altra cosa!)

E quali, invece, gli svantaggi?

  • Manuali inesistenti
  • Documentazione delle classi completa ma poco dettagliata
  • Inadatto ai siti web veri e propri (gli autori stessi dichiarano che il framework è stato progettato per applicazioni web di tipo gestionale)
  • Impossibilità di avere il completo controllo del codice, ma questa è una caratteristica comune a tutti i framework, anzi quelli più complessi sono anche molto più criptici

Conclusioni:

Il framework P4A rappresenta un ottimo strumento per la realizzazione di applicazioni web, anche complesse, soprattutto di tipo gestionale. La semplicità di installazione ed utilizzo ripagano ampiamente la minore dotazione e flessibilità rispetto ai blasonati Zend Framework, Symphony e CakePhp. Seppure la dotazione manualistica è inesistente, l’ampio numero di esempi disponibili in rete e la presenza di un forum abbastanza attivo supplisce a questa mancanza. Infine l’attività di manutenzione e sviluppo da parte degli autori insieme al fatto che il prodotto ha ottenuto il ragguardevole numero di 130.000 download è garanzia di vitalità e supporto anche nel futuro.

Riferimenti ed approfondimenti: