{"word_count": 933, "character_count": 7998}
En fin de semaine dernière, je commencais surveiller et prévenir: L'ère de la pénalité prédictive de Nicolas Bourgoin, et au début, il évoque des mesures de sécurité renforcées dans un rayon de 20km des gares, des aéroports et des ports, qui pourraient être prises dans le cadre de la réforme de lois sur la sécurité intérieure.  Cette information était reprise récemment sur rtl,Le périmètre est aussi agrandi : les contrôles pourront avoir lieu aux abords des gares internationales (et non plus seulement à l'intérieur) ainsi que dans un rayon de 20 kilomètres autour des aéroports et des ports.ou sur le site de l'obsces opérations de contrôles seront mises en place "aux abords" de 373 gares, ports et aéroports, dans un rayon de 20 kilomètres. Une extension considérable puisque jusqu'à présent, ces contrôles restaient cantonnées à l'intérieur de ces espaces accessibles au public.En lisant le livre, je trouvais incroyable cette histoire de 20km, car tout le monde habite à moins de 20km d'une gare (ne sachant trop quelles pouvaient être ces 373 gares, je pars du fait que toutes les gares pourraient être concernées). C'est un peu ce que note la cimade, en écrivantLe projet de loi prévoit ainsi de permettre les contrôles d’identité aux frontières pour une durée de 12 heures (contre 6 aujourd’hui), de les élargir « aux abords » de 373 gares, ports et aéroports, ainsi que dans un rayon de 20 km des 118 points de passages frontaliers. Bien au-delà des simples frontières de l’Hexagone, c’est presque tout le territoire qui est couvert. Le dispositif porte ainsi une atteinte disproportionnée aux droits et libertés des personnes. Des villes entières, comme Paris et toute la région Île-de-France, Lyon, Nantes, Rennes, Bordeaux, Montpellier, Toulouse ou Marseille seraient soumises à un régime de légalisation du contrôle au faciès. Des personnes assimilées par la police comme étant étrangères, quelle que soit leur situation en France, risquent ainsi d’être les victimes de ces contrôles d’identité.Etant d'un naturel (très) sceptique, j'ai voulu vérifier par moi même, non seulement en terme de surface (ce qui est dit ici) mais surtout en terme de population. La première étape est de récupérer la liste des gares géolocalisées, sur https://ressources.data.sncf.com/. On peut aussi récupérer la liste des aéroports sur https://www.data.gouv.fr/ si on veut aller plus loin. Mais contentons nous des gares pour l'instant. La seconde étape est de récupérer les contours des communes et leur population. En fait, plus que la superficie, ce qui m'intéresse le plus, c'est le nombre de personnes qui y habitent. On peut trouver un tel fichier sur https://www.data.gouv.fr/ là encore. Mais commencons par charger tous nos packages de cartographie,library(maptools)library(rgeos)library(rgdal)library(ggplot2)library(plyr)library(maptools)On peut ensuite récupérer les données des garesloc = "/home/arthur/referentiel-gares-voyageurs.shp"gare = readOGR(loc)Pour regarder où sont ces gares, on récupère un fond de carteProj = CRS("+proj=longlat +datum=WGS84")France = readShapePoly("departements-20140306-100m.shp", verbose=TRUE, proj4string=Proj)metropolitaine = as.character(1:95)metropolitaine[1:9] = paste("0",1:9,sep="")France = France[France$code_insee%in%metropolitaine,]On utilise aussi une base avec les contours des communes,loc2 = "/home/arthur/communes-20150101-5m.shp"communes_lim = readOGR(loc2)communes_lim = spTransform(communes_lim, CRS("+proj=longlat +datum=WGS84"))et une base pour la population des communes base = read.csv("http://freakonometrics.free.fr/popfr19752010.csv",header=TRUE)base$insee = base$dep*1000+base$comCette fois on est prêt. Considérons un rayon de 20kmr=20On va constituer tous les polygônes correspondant à une cercle de rayon 20km, centré sur les gares francaisesPL = data.frame(i=1:(3000*120),id=rep(1:3000,each=120),lon=NA,lat=NA) for(i in 1:nrow(gare)){   x=as.numeric(as.character(gare$longitude_w[i]))   y=as.numeric(as.character(gare$latitude_wg[i]))   vx=c(x+u*r/111,x+rev(u)*r/111)   vy=c(y+v*r/111,y-rev(v)*r/111)   polygon(vx,vy,border=NA,col="blue",pch=19)   PL[PL$id==i,2:4]=data.frame(id=i,lon=vx,lat=vy) }(comme 1 degré fait environ 111km). On doit alors bricoler un peu pour constituer une collection de polygones, que l'on pourra ensuite manipuler à notre guisePL=PL[!is.na(PL$lat),] PLdf=PL[,c(3,4,2)] PLdf[,3]=as.factor(PLdf[,3]) PL_list &#60;- split(PLdf, PL$id) PL_list &#60;- lapply(PL_list,   function(x) { x["id"] &#60;- NULL; x }) PPL &#60;- sapply(PL_list, Polygon) PPL1 &#60;- lapply(seq_along(PPL),     function(i) Polygons(list(PPL[[i]]),        ID = names(PL_list)[i]  ))PPLs &#60;- SpatialPolygons(PPL1, proj4string = CRS("+proj=longlat +datum=WGS84") )Si on visualise ces périmètres de sécurité, on obtientG=gUnionCascaded(PPLs)F=gUnionCascaded(France)FG=gIntersection(F,G)plot(F)plot(G,add=TRUE,col="blue")soit, si on regarde l'intersection entre les deux,plot(F)plot(FG,add=TRUE)Pour trouver la population dans cette vaste région, on va supposer la population uniformément répartie sur une commune. Et on regarde la proportion de la surface de la commune qui est à moins de 20km d'une gare (c'est en gros la technique qu'on utilisait dans Kernel density estimation based on Ripley's correction pour corriger d'effet de bords dans du lissage spatial, sur des cartes, avec Ewen Gallic). Bref, pour une commune donnée, on a le code suivant, qui permet de récuperer la proportion de la surface située à moins de 20km d'une gare, et sa populationf=function(i){insee=as.numeric(as.character(communes_lim@data$insee[i]))  POPULATION=base[base$insee==insee,"pop_2010"]  B_list=list()  for(j in 1:length(communes_lim@polygons[[1]]@Polygons)){ B_list[[j]]=data.frame(communes_lim@polygons[[i]]@Polygons[[j]]@coords,id=j)}  B_list &#60;- lapply(B_list,function(x) { x["id"] &#60;- NULL; x })  BL &#60;- sapply(B_list, Polygon)  BL1 &#60;- lapply(seq_along(BL), function(i) Polygons(list(BL[[i]]),ID = names(PL_list)[i]  ))  BLs &#60;- SpatialPolygons(BL1, proj4string = CRS("+proj=longlat +datum=WGS84") )   t=try(FGB&#60;-gIntersection(BLs,FG),silent=TRUE)  t1=try(l&#60;-length(BLs@pointobj@coords),silent=TRUE)  if((!inherits(t1, "try-error"))){     a_list=list()    for(j in 1:length(BLs@pointobj@coords)){      a_list[[j]]=BLs@polyobj@polygons[[j]]@area}    a1=sum(unlist(a_list))}  if((inherits(t1, "try-error"))){     a1=BLs@polygons[[1]]@area}  a2=0  if(!is.null(FGB)){t2=try(l&#60;-length(FGB@pointobj@coords),silent=TRUE) if((!inherits(t2, "try-error"))){ a_list=list() for(j in 1:length(FGB@pointobj@coords)){ a_list[[j]]=FGB@polyobj@polygons[[j]]@area} a2=sum(unlist(a_list))} if((inherits(t2, "try-error"))){ a2=FGB@polygons[[1]]@area}} p=c(1,NA,0) if((!inherits(t, "try-error"))&#38;(!is.null(t))&#38;(length(POPULATION)==0)) p=c(a2/a1,a1,0) if((!inherits(t, "try-error"))&#38;(!is.null(t))&#38;(length(POPULATION)&#62;0) ) p=c(a2/a1,a1,POPULATION) cat(i,insee,sum(unlist(lapply(B_list,nrow))),p,"\n")  return(p)}On n'a plus qu'à faire tourner sur notre 36000 communes,F = lapply(unique(communes_lim_df$id),f)On obtient alors&#62; F0=F[!is.na(F[,2]),]&#62; sum(F0[,1]*F0[,2])/sum(F0[,2])[1] 0.9031736&#62; F=F1&#62; sum(F[,1]*F[,3])/sum(F[,3])[1] 0.9661656Autrement dit, dans un rayon de 20km des gares (seulement), on a 90% du territoire, et plus de 95% de la population. Si on passe à 10km, on note que l'on couvre environ 75% du territoire, et plus de 90% de la population, et en passant à 5km, on couvre moins de 50% de la surface du territoire, et environ 75% de la population.On retrouve des résultats du même ordre que ceux obtenus dans un précédant billet, qui établissait que 80% de la population francaise était à moins de 3km d'une agence bancaire. On pourrait bien sûr rajouter les ports, et les aéroports, et surprimer quelques gares... mais je doute qu'on ait une conclusion très différente... Après, les codes sont disponibles, il suffit de les adapter sur une autre base de centres de cercles...