Angular, Google Maps autocomplete and geocoder

Voici  un module avec quelques  directives AngularJS  a utiliser avec Google Map V3 . Bien sur il existe un module complet mais aussi complexe avec AngularUI .Ici l avantage te la simplicité et la possibilité de modifier ce code a votre besoin.

La première directive est pour afficher la carte google : on peut lui passer plusieurs paramètres comme la liste des markers , le centre, les coordonnée géographique ...

<div ng-map style="height:400px;margin:12px;box-shadow:0 3px 25px black;"
        center="{ lat: 40, lon: -73 }"
        markers="[]"
 geo-coord="mapcoord"
 ></div>
La deuxième directive est le geoCoder qui consiste a chercher une adresse et l afficher sur la carte

<div class="input-append text-right"
             ng-geocoder
             ng-model="address"
             geo-coord="mapcoord">
</div>
La troisième directive est AddressAutocomplete  qui  procure une assistance a la saisie  des adresses et remplit un formulaire automatiquement .


<form class="form-horizontal" role="form">
   <div class="form-group">
   <label class="control-label col-sm-2 " for="email">Saisir adresse:</label>
   <div  class="col-sm-10">
 <input type="email" class="form-control" ng-model="fullAddress" 
    ng-address-autocomplete  
    street-name="streetName"
    city="city"
    zip-code="zipCode"
    country="country"
    street-number="streetNumber" />
   </div>
   </div>
    <br/> 
   <div class="form-group">
    <label class="control-label  " for="email">Numero:</label>
    <div>
    <input type="text" class="form-control" ng-model="streetNumber"/>
    </div>
    </div> 
    <br/>
    <div class="form-group">
     <label class="control-label  " for="email">Rue:</label>
     <div>
      <input type="text" class="form-control" ng-model="streetName"/>
     </div>
     </div> 
     <br/> 
     <div class="form-group">
     <label class="control-label  " for="email">Ville:</label>
     <div>
     <input type="text" class="form-control" ng-model="city"/>
     </div>
     </div> 
      <br/>
      <div class="form-group">
      <label class="control-label  " for="email">Code postal:</label>
      <div>
     <input type="text" class="form-control" ng-model="zipCode"/>
     </div>
     </div>
     <br/> 
     <div class="form-group">
       <label class="control-label  " for="email">Pays:</label>
     <div>
      <input type="text" class="form-control" ng-model="country"/>
    </div>
    </div> 
   </form>
Voici l exemple complet : Plunker
 <!DOCTYPE html>
<html   id='ng-app'  >
<head>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css">

     
</head>

<body  ng-app="app" ng-controller="appCtrl">
 <h3 class="text-center">   Angular, Google Maps autocomplete and geocoder </h3>
<div class="text-center">
        <div class="input-append text-right" ng-geocoder ng-model="address" geo-coord="mapcoord">
        </div>
    </div>
     

    <!-- map -->
    <div ng-map style="height:400px;margin:12px;box-shadow:0 3px 25px black;"
        center="{ lat: 40, lon: -73 }"
        markers="[]"
  map="myMap"
  geo-coord="mapcoord"
    > 
    </div>
  <br/> <br/>
  
 <form class="form-horizontal" role="form">
  <div class="form-group">
  <label class="control-label col-sm-2 " for="email">Saisir adresse:</label>
  <div  class="col-sm-10">
   <input type="email" class="form-control" ng-model="fullAddress"  
         ng-address-autocomplete   
         street-name="streetName" 
         city="city"
         zip-code="zipCode"
         country="country"
         street-number="streetNumber" />
  </div>
  </div>
  <br/>  
  <div class="form-group">
  <label class="control-label  " for="email">Numero:</label>
  <div>
   <input type="text" class="form-control" ng-model="streetNumber"/> 
  </div>
  </div>  
  <br/>
  <div class="form-group">
  <label class="control-label  " for="email">Rue:</label>
  <div>
   <input type="text" class="form-control" ng-model="streetName"/> 
  </div>
  </div>  
  <br/> 
  <div class="form-group">
  <label class="control-label  " for="email">Ville:</label>
  <div>
   <input type="text" class="form-control" ng-model="city"/> 
  </div>
  </div>   
   <br/>
   <div class="form-group">
  <label class="control-label  " for="email">Code postal:</label>
  <div>
   <input type="text" class="form-control" ng-model="zipCode"/> 
  </div>
  </div>
  <br/>  
   <div class="form-group">
  <label class="control-label  " for="email">Pays:</label>
  <div>
   <input type="text" class="form-control" ng-model="country"/> 
  </div>
  </div> 
   </form>
  
     

     
</body>
<script src="http://maps.googleapis.com/maps/api/js?key=&sensor=false&extension=.js&libraries=places"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.16/angular.min.js"></script>

 <script>
 
 angular.module("ngMapModule", [])
  .directive("ngMap", function ($timeout) {
    return {
        restrict: "EA",
        replace: true,
        template: "<div></div>",
        scope: {
      geoCoord:"=",
            center: "=",        //{latitude: 10, longitude: 10 } .
            markers: "=",       //[{ lat: 10, lon: 10, name: "sName" }] .
            zoom: "@",          //(1  zoomed out, 25  zoomed in).
            mapTypeId: "@",     //(roadmap, satellite, hybrid, terrain).
            panControl: "@",    //show a pan control on the map.
            zoomControl: "@",   //show a zoom control on the map.
            scaleControl: "@"   //show scale control on the map.
        },
  controller:function($scope){
  $scope.updateMap=function(elem) {
                var options =getOptions();
                $scope.map = new google.maps.Map(elem, options);
                $scope.updateMarkers();
                google.maps.event.addListener($scope.map, 'center_changed', function () {
                    if ($scope.initCenter) clearTimeout($scope.initCenter);
                    $scope.initCenter = $timeout(function () {
                        if ($scope.center) {
                            if ($scope.map.center.lat() != $scope.center.lat ||
                                $scope.map.center.lng() != $scope.center.lon) {
                                $scope.center = { lat: $scope.map.center.lat(), lon: $scope.map.center.lng() };
                                if (!$scope.$$phase) $scope.$apply("center");
                            }
                        }
                    }, 500);
                });
            }
  $scope.updateMarkers=function() {
                if ($scope.map && $scope.markers) {
                    if ($scope.currentMarkers != null) {
                        for (var i = 0; i < $scope.currentMarkers.length; i++) {
                            $scope.currentMarkers[i] = m.setMap(null);
                        }
                    }
                    $scope.currentMarkers = [];
                    var markers = $scope.markers;
                    if (angular.isString(markers)) markers = $scope.$eval($scope.markers);
                    for (var i = 0; i < markers.length; i++) {
                        var m = markers[i];
                        var loc = new google.maps.LatLng(m.lat, m.lon);
                        var mm = new google.maps.Marker({ position: loc, map: $scope.map, title: m.name });
                        $scope.currentMarkers.push(mm);
                    }
                }
            }
  $scope.getLocation=function(loc) {
                if (loc == null) return new google.maps.LatLng(40, -73);
                if (angular.isString(loc)) loc = $scope.$eval(loc);
                return new google.maps.LatLng(loc.lat, loc.lon);
            }
  function getOptions() {
    var options =
       {
        center: new google.maps.LatLng(40, -73),
        zoom: 6,
        mapTypeId: "roadmap"
       };
    if ($scope.center) options.center = $scope.getLocation($scope.center);
    if ($scope.zoom) options.zoom = $scope.zoom / 1;
    if ($scope.mapTypeId) options.mapTypeId = $scope.mapTypeId;
    if ($scope.panControl) options.panControl = $scope.panControl;
    if ($scope.zoomControl) options.zoomControl = $scope.zoomControl;
    if ($scope.scaleControl) options.scaleControl = $scope.scaleControl;
    return options;
   }
  },
        link: function (scope, element, attrs) {
            var scopeArray = ["markers", "mapTypeId", "panControl", "zoomControl", "scaleControl"],toCenter  ;
            for (var i = 0, cnt = scopeArray.length; i < scopeArray.length; i++) {
                scope.$watch(scopeArray[i], function () {
                    cnt--;
                    if (cnt <= 0) {
                        scope.updateMap(element[0] );
                    }
                });
            }
            scope.$watch("zoom", function () {
                if (scope.map && scope.zoom)
                    scope.map.setZoom(scope.zoom / 1);
            });
            scope.$watch("center", function () {
                if (scope.map && scope.center)
                    scope.map.setCenter(scope.getLocation(scope.center));
            });
   scope.$watch("geoCoord", function (val) {
                if (val){
    var coord=new google.maps.LatLng(val.lat, val.lon);
    scope.map.setCenter(coord);
    scope.map.setZoom(12);
    }
                    
            });
   
        }
    };
}).directive("ngGeocoder", function () {
    return {
        restrict: "EA",
  template: '<input   type="text"  ng-model="ngModel" style="width:500px"/>'+
            '<button class="btn" type="button" ng-click="geoCode()" ng-disabled="address.length == 0" title="address" >'+
              '&nbsp;<i class="icon-search"></i></button>',
        scope: {
      ngModel:"=",
   geoCoord:"="
           },
  controller:function($scope){
       
   $scope.geoCode = function () {
    if ($scope.ngModel && $scope.ngModel.length > 0) {
     if (!$scope.geocoder) $scope.geocoder = new google.maps.Geocoder();
     $scope.geocoder.geocode({ 'address': $scope.ngModel }, function (results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
       var location = results[0].geometry.location;
       if (!$scope.$$phase) $scope.$apply(function(){
       $scope.geoCoord={ lat: location.lat(), lon: location.lng() }
       $scope.ngModel = results[0].formatted_address;
       });
       
        
      } else {
       alert("Sorry,  address produced no results.");
      }
     });
    }
   };
   
   
  },
  link: function (scope, element, attrs) {
  
  
  }
  }
 }).directive("ngAddressAutocomplete", function () {
    return {
        restrict: "EA",
        scope: {
      ngModel:"=",
      city:"=",
      zipCode:"=",
      country:"=",
   streetName:"=",
   streetNumber:"="
           },
  link: function (scope, element, attrs) {
   var autocomplete = new google.maps.places.Autocomplete(element[0]);
   google.maps.event.addListener(autocomplete, 'place_changed', function() {  
   var place = autocomplete.getPlace();  
    
   if (!scope.$$phase) scope.$apply(function(){
     var componenents=place.address_components||[];
     for(var i=0;i<componenents.length;i++){
      var item=componenents[i];
      if(item&&item.types){
         if(/route/gi.test(item.types[0])){
         scope.streetName =componenents[1].long_name;
         }
         if(/country/gi.test(item.types[0])){
         scope.country =item.long_name;
         }
         if(/street_number/gi.test(item.types[0])){
         scope.streetNumber =item.long_name;
         }
         if(/postal_code/gi.test(item.types[0])){
         scope.zipCode =item.long_name;
         }
         if(/dministrative_area_level_2|city/gi.test(item.types[0])){
         scope.city =item.long_name;
         }
        }
     }
   });
      
   })
  }
  }
 })
 var app = angular.module("app", ["ngMapModule"]);
     app.controller("appCtrl", function ($scope) {
  
  $scope.mapcoord
});
 </script>
</body>
Voir aussi sur Plunker

Gestion des Performance d'une application Le cube de mise a l'échelle d'une ou Application Cube of scale

           De nos jours les applications sont souvent conçues pour répondre a une grande charge d'utilisation et pour manipuler une masse importante de de données. Ajoute a cela des exigences de performance de plus en plus croissantes.
Pour répondre a ces exigences du point de vue architecture, développement et reploiement d'application, parfois on se demande par quel bout commencer vu que les latences dans une application peuvent être a tous les niveaux:

  • Les performances au niveaux du code lui même 
  • La charge due a l'utilisation croissante 
  • La masse et l’accès aux données.
  • La volumétries des données 
  • Les latences réseaux 
  • Et bien d'autres ...
Pour être un peu machiavélique, diviser pour régner on peut repartir cette situation sur trois axes :

  1. L'axe des X : La charge d’utilisation : combien de serveurs sont nécessaires pour répondre a la charge.
  2. L'axes des Y : Le code et l'architecture de l application répondent ils au besoin ou doit on repartir la'application sur plusieurs services et petites sous application.
  3. L'axe des Y  : Les données combien de serveurs faut-il pour répondre a la charge combien de clusters , combien de shardes ....

Si on représente ces trois points sur un axe on obtient ce que on appel le cube de mise en échelle qui nous renseigne sur les direction a prendre pour la mise a l echelle

                              


Optimisation des Performances dans une application ASP.NET

Introduction

L'Optimisation des performances d'une application d'entreprise reste un domaine large. Les latences peuvent être détectés a tous les niveaux de l'application sinon du système complet .Dans ce billet je vais signaler quelques pratiques et même habitudes a prendre lors du développement d'une application Web en général et d'une application ASP.NET Webform ou MVC . On s’intéresse généralement aux performances d'une application lorsque celle ci  devient complexe et grossit , a ce moment la on entame un travail de refactoring et d’investigation pour trouver le coupable qui ralentit ou qui empêche le système de bien fonctionner. A l inverse il est de bonne pratique de considérer les performances dès le début du projet et a tout les niveaux architecture, développement, données ... Pour commencer cette série de billets dédiées aux performances des applications .NET , je vais commencer par la partie Web et principalement quelques conseils pour optimiser le chargement des ressources html/JS/CSS, la configuration ASP.NET et le server IIS .


Optimisation du chargement de la page Web

Le temps de chargement de la page web est précieux, Selon les études de Google les utilisateurs abandonnent la page si elle ne se charge pas dans moins de 5 secondes. Voici quelques astuces pour optimiser le chargement de la page :
  Réduire le nombre requêtes HTTP : 80% du temps de chargement d'une page web se passe sur le navigateur et principalement pour télécharger les script, images , css ...
- Combiner et minifier les fichier JavaScript et CSS.
- Utiliser les CSS sprites : Minifier les images et utiliser les propriétés background-image et packground-position pour affichage
- Utiliser Les images enligne : Utiliser la propriété src="data: ...."  ce qui vas augmenter la taille du fichier HTML mais réduit le nombre de requêtes HTTP
- Utiliser les CDN : Certains fichier statiques comme les librairie JS, CSS et même certaines images  de préférence de les mettre sur un CDN comme ça ils seront cache par le navigateur .
Ajouter les entêtes d'expiration : Pour le contenu statique utiliser "Never expire". Pour le contenu dynamique mettre le Cache-Control approprié.
- Utiliser la compression GZIP  : La compression réduit la taille et le contenu de la réponse HTTP
- Les CSS dans l entête de la page
- Les JavaScript dans le bas de la page HTML 
- Eviter les expression CSS : color:expression(....
- Si possible mettre les contenus statiques  externes a l'application
- Eviter la redirection de page  
- Eviter la duplication des scripts sur la même page
- Utiliser le paramètre  Etag.
- Faire en sorte que même les requêtes Ajax soient cachées
- De préférence d’utiliser les appels GET par Ajax
- Charger que le contenu nécessaire a la page
- Réduire la taille du DOM sur la page
- Réduire le nombre de iframe sur la page
- Réduire la taille des cookies
- Utiliser la balise <link> non la directive CSS @import 
- Si possible moins de 25k pour un fichier statique : Iphone  limite le cache à 25k
- Eviter les attribut src vides : Cela fait un appel server malgré.


Optimisation de la configuration ASP.NET

Process Model : ASP.NET est configuré par défaut dans le fichier machine.config  dans lequel est configuré le Process Model d'ASP.NET
 <system.web> 
     < processModel autoConfig =" true" > 
Cette configuration  doit être retouchée pour des besoins précis  pour specifier le nombre de thread ,le nombre de requêtes ... Voici toute la configuration pour le Porcess Model :
<processModel
enable="true" timeout="Infinite" idleTimeout="Infinite" shutdownTimeout="00:00:05" requestLimit="Infinite" requestQueueLimit="5000" restartQueueLimit="10" memoryLimit="60" webGarden="false" cpuMask="0xffffffff" userName="machine" password="AutoGenerate" logLevel="Errors" clientConnectedCheck="00:00:05" comAuthenticationLevel="Connect" comImpersonationLevel="Impersonate" responseDeadlockInterval="00:03:00" responseRestartDeadlockInterval="00:03:00" autoConfig="false" maxWorkerThreads="100" maxIoThreads="100" minWorkerThreads="40" minIoThreads="30" serverErrorMessageFile="" pingFrequency="Infinite" pingTimeout="Infinite" asyncOption="20" maxAppDomains="2000" />
Le cache server : a Défaut d'avoir une solution de cache server dédiée  utiliser le cache ASP.NET
Optimiser le pipline ASP.NET : ASP.NET par defaut charge certain module comme la session l authentication...  voici la liste de certains modules chargées das ASP.NET  qui sont configurés dans le fichier machine.config

  
  
  
  
  
  
  
  


on peut déjà enlever certains modules qui sont rarement utilisés mais qui consomme du temps et des ressources .

        
         
         
         
         
         

Optimisation  lors du développement : dans certains cas on remarque que l application ASP.NET est lente a compiler et a se charger après chaque compilation voici quelques astuce pour améliorer ça :
    Configurer Appool comme Suspend et AlwaysRunning 
iis1
Optimiser le temps de compilation dans Visual Studio
vs1
Configurer l'optimisation de la compilation : dans Web.config


Certaines astuces pour réduire le temps de démarrage de l application :
Enlever les projets non utilisées dans l'application. Utiliser Razor Generator  pour la près compilation : Razor Genaration peut etre installe comme  un package Nuget et le configurer par la suite dans les fichiers .csproj

 Conclusion

Voila pour cette fois ci quelques astuces a prendre en compte dès le début des développements dans un prochain Billet je reviendrai en détails sur les optimisations du code et la maniéré de tester les performances d'une application plus loin je parlerai aussi de manière  générale sur les performances d'une application .NET Web