This repository has been archived by the owner on Oct 12, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Super flexible GeoAutoComplete with Solr.html
343 lines (278 loc) · 29.6 KB
/
Super flexible GeoAutoComplete with Solr.html
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
<!DOCTYPE html>
<html lang="en">
<head>
<title>Super flexible GeoAutoComplete with Solr</title>
<meta charset="utf-8" />
<link href="https://techblog.mappy.com/feeds/rss.xml" type="application/atom+xml" rel="alternate" title="Mappy Labs Full Atom Feed" />
<link href="https://techblog.mappy.com/feeds/solr/rss.xml" type="application/atom+xml" rel="alternate" title="Mappy Labs Categories Atom Feed" />
<!-- Mobile viewport optimized: j.mp/bplateviewport -->
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="./theme/gumby.css" />
<link rel="stylesheet" type="text/css" href="./theme/style.css" />
<link rel="stylesheet" type="text/css" href="./theme/pygment.css" />
<link rel="icon" type="image/png" href="images/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32">
</head>
<body id="index" class="home">
<div class="container">
<header id="banner" class="body">
<h1><a href="/"><img src="/images/logo.png" /> Labs</a></h1>
<div id="navigation" class="navbar row">
<a href="#" gumby-trigger="#navigation > ul" class="toggle"><i class="icon-menu"></i></a>
</div>
</header><!-- /#banner -->
<div class="row">
<section id="content" class="body">
<div class="row">
<div class="ten columns">
<header>
<h2 class="entry-title">
<a href="./Super flexible GeoAutoComplete with Solr.html" rel="bookmark"
title="Permalink to Super flexible GeoAutoComplete with Solr">Super flexible GeoAutoComplete with Solr</a></h2>
</header>
<footer class="post-info">
<abbr class="published" title="2014-02-25T00:00:00+01:00">
Tue 25 February 2014
</abbr>
<address class="vcard author">
By <a class="url fn" href="./author/jerome-bernardes.html">Jérôme Bernardes</a>
</address>
</footer><!-- /.post-info -->
<div class="entry-content">
<h1>Super flexible GeoAutoComplete with Solr</h1>
<p>In an inspiring article <a href="http://www.cominvent.com/2012/01/25/super-flexible-autocomplete-with-solr/">Cominvent AS</a> presented us how to use <a href="https://lucene.apache.org/solr/">Solr</a> power to implement autocomplete feature. We are going to present you how to add a geographical component to this suggestion.</p>
<ol>
<li>Download and unpack Solr if you have not already <a href="http://www.apache.org/dyn/closer.cgi/lucene/solr/">http://www.apache.org/dyn/closer.cgi/lucene/solr/</a></li>
<li>Download and unpack <a href="./resources/mappy-autocomplete.zip">mappy-geoautocomplete.zip</a></li>
<li>Cd to the autocomplete folder, open README.TXT and follow the instructions. When done you will have Solr up and running with the example data indexed into the “acgeo” core.</li>
<li>When you browse to http://localhost:8000/ and start typing, you will see a map and countries and cities suggested</li>
</ol>
<p>As ranking factor, we use a combination of the population of the countries and cities, the textual relevancy and, that is the main point of our article, the proximity to the center of the map we display.
As the use of population and text relevancy has been explained in <a href="http://www.cominvent.com/2012/01/25/super-flexible-autocomplete-with-solr/">Cominvent As</a> article, let's focus on the geographical part of the score.
First we need to store the coordinates for each city and country. Thus we add longitude (lng) and latitude (lat) fields in our schema.xml</p>
<div class="highlight"><pre><span></span><span class="nt"><field</span> <span class="na">name=</span><span class="s">"lng"</span> <span class="na">type=</span><span class="s">"float"</span> <span class="na">indexed=</span><span class="s">"true"</span> <span class="na">stored=</span><span class="s">"true"</span> <span class="na">omitNorms=</span><span class="s">"true"</span> <span class="na">required=</span><span class="s">"true"</span><span class="nt">/></span>
<span class="nt"><field</span> <span class="na">name=</span><span class="s">"lat"</span> <span class="na">type=</span><span class="s">"float"</span> <span class="na">indexed=</span><span class="s">"true"</span> <span class="na">stored=</span><span class="s">"true"</span> <span class="na">omitNorms=</span><span class="s">"true"</span> <span class="na">required=</span><span class="s">"true"</span><span class="nt">/></span>
</pre></div>
<p>Then we need to modify solrconfig.xml</p>
<div class="highlight"><pre><span></span><span class="nt"><requestHandler</span> <span class="na">class=</span><span class="s">"solr.SearchHandler"</span> <span class="na">name=</span><span class="s">"acgeo"</span> <span class="na">default=</span><span class="s">"true"</span> <span class="nt">></span>
<span class="nt"><lst</span> <span class="na">name=</span><span class="s">"defaults"</span><span class="nt">></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"defType"</span><span class="nt">></span>edismax<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"rows"</span><span class="nt">></span>10<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"fl"</span><span class="nt">></span>*,score<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"qf"</span><span class="nt">></span>name^30 textng^50.0<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"pf"</span><span class="nt">></span>textnge^50.0<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"bf"</span><span class="nt">></span>product(log(sum(population,1)),100)^20<span class="nt"></str></span>
<span class="c"><!-- Define relative importance between types. May be overridden per request by e.g. &personboost=120 --></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"boost"</span><span class="nt">></span>product(product(map(query($type1query),0,0,1,$type1boost),map(query($type2query),0,0,1,$type2boost),map(query($type3query),0,0,1,$type3boost),map(query($type4query),0,0,1,$type4boost),$typeboost), geoboost($eps,$mu,$lat_min,$lng_min,$lat_max,$lng_max,lat,lng))<span class="nt"></str></span>
<span class="nt"><double</span> <span class="na">name=</span><span class="s">"typeboost"</span><span class="nt">></span>1.0<span class="nt"></double></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"type1query"</span><span class="nt">></span>type:"Countries"<span class="nt"></str></span>
<span class="nt"><double</span> <span class="na">name=</span><span class="s">"type1boost"</span><span class="nt">></span>0.9<span class="nt"></double></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"type2query"</span><span class="nt">></span>type:"Cities"<span class="nt"></str></span>
<span class="nt"><double</span> <span class="na">name=</span><span class="s">"type2boost"</span><span class="nt">></span>0.5<span class="nt"></double></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"type3query"</span><span class="nt">></span>type:"NA"<span class="nt"></str></span>
<span class="nt"><double</span> <span class="na">name=</span><span class="s">"type3boost"</span><span class="nt">></span>0.0<span class="nt"></double></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"type4query"</span><span class="nt">></span>type:"NA"<span class="nt"></str></span>
<span class="nt"><double</span> <span class="na">name=</span><span class="s">"type4boost"</span><span class="nt">></span>0.0<span class="nt"></double></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"lng_min"</span><span class="nt">></span>0<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"lat_min"</span><span class="nt">></span>0<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"lng_max"</span><span class="nt">></span>0<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"lat_max"</span><span class="nt">></span>0<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"eps"</span><span class="nt">></span>0.1<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"mu"</span><span class="nt">></span>1.0<span class="nt"></str></span>
<span class="nt"><str</span> <span class="na">name=</span><span class="s">"debugQuery"</span><span class="nt">></span>false<span class="nt"></str></span>
<span class="nt"></lst></span>
<span class="nt"></requestHandler></span>
</pre></div>
<p>Now that our Solr is prepared to manage request with coordinates, we obviously have to send it.</p>
<div class="highlight"><pre><span></span><span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span>
<span class="s1">'url'</span><span class="o">:</span> <span class="s1">'http://localhost:8983/solr/acgeo/select?'</span><span class="p">,</span>
<span class="s1">'delay'</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="s1">'dataType'</span><span class="o">:</span> <span class="s2">"jsonp"</span><span class="p">,</span>
<span class="s1">'data'</span><span class="o">:</span> <span class="p">{</span>
<span class="s1">'q'</span><span class="o">:</span> <span class="nx">request</span><span class="p">.</span><span class="nx">term</span><span class="p">,</span>
<span class="nx">wt</span><span class="o">:</span> <span class="s2">"json"</span><span class="p">,</span>
<span class="s2">"json.wrf"</span> <span class="o">:</span> <span class="s2">"callback"</span><span class="p">,</span>
<span class="s2">"rows"</span><span class="o">:</span> <span class="mi">5</span><span class="p">,</span>
<span class="s1">'lat_min'</span><span class="o">:</span> <span class="nx">myMap</span><span class="p">.</span><span class="nx">getBounds</span><span class="p">().</span><span class="nx">getSouth</span><span class="p">(),</span>
<span class="s1">'lat_max'</span><span class="o">:</span> <span class="nx">myMap</span><span class="p">.</span><span class="nx">getBounds</span><span class="p">().</span><span class="nx">getNorth</span><span class="p">(),</span>
<span class="s1">'lng_min'</span><span class="o">:</span> <span class="nx">myMap</span><span class="p">.</span><span class="nx">getBounds</span><span class="p">().</span><span class="nx">getWest</span><span class="p">(),</span>
<span class="s1">'lng_max'</span><span class="o">:</span> <span class="nx">myMap</span><span class="p">.</span><span class="nx">getBounds</span><span class="p">().</span><span class="nx">getEast</span><span class="p">()</span>
<span class="p">},</span>
<span class="p">...)</span>
</pre></div>
<p>Even if Solr <a href="http://wiki.apache.org/solr/FunctionQuery">FunctionQuery</a> gives us a set of possibility, it may not fit our particular needs. In that case we can easily extend the list of available functions by writing our own in Java. Let's implement a <code>geoboost</code> function that is equal to <code>1</code> inside a given bounding box, and that is decreasing until <code>epsilon</code> outside the bounding box. The decreasing speed is configurable via parameter <code>mu</code> (A plot of this function is available <a href="./images/geoboost.png">here</a>)</p>
<div class="highlight"><pre><span></span>geoboost(epsilon, mu, lat_min, lng_min, lat_max, lng_max)
</pre></div>
<p>We have to write a parser (i.e. a class implementing <a href="http://wiki.apache.org/solr/SolrPlugins#ValueSourceParser">org.apache.solr.search.ValueSourceParser</a> that reads the value from the left to the right)</p>
<div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">class</span> <span class="nc">GeoBoostValueParser</span> <span class="kd">extends</span> <span class="n">ValueSourceParser</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">ValueSource</span> <span class="nf">parse</span><span class="o">(</span><span class="n">FunctionQParser</span> <span class="n">fp</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">SyntaxError</span> <span class="o">{</span>
<span class="kt">float</span> <span class="n">eps</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="na">parseFloat</span><span class="o">();</span>
<span class="kt">float</span> <span class="n">mu</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="na">parseFloat</span><span class="o">();</span>
<span class="kt">float</span> <span class="n">latmin</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="na">parseFloat</span><span class="o">();</span>
<span class="kt">float</span> <span class="n">lngmin</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="na">parseFloat</span><span class="o">();</span>
<span class="kt">float</span> <span class="n">latmax</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="na">parseFloat</span><span class="o">();</span>
<span class="kt">float</span> <span class="n">lngmax</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="na">parseFloat</span><span class="o">();</span>
<span class="n">ValueSource</span> <span class="n">lat</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="na">parseValueSource</span><span class="o">();</span>
<span class="n">ValueSource</span> <span class="n">lng</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="na">parseValueSource</span><span class="o">();</span>
<span class="k">return</span> <span class="k">new</span> <span class="n">GeoBoostFunction</span><span class="o">(</span><span class="n">eps</span><span class="o">,</span><span class="n">mu</span><span class="o">,</span><span class="n">latmin</span><span class="o">,</span><span class="n">lngmin</span><span class="o">,</span><span class="n">latmax</span><span class="o">,</span><span class="n">lngmax</span><span class="o">,</span><span class="n">lat</span><span class="o">,</span><span class="n">lng</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></div>
<p>And the class that does the real job, where the result is returned by <code>public FunctionValues getValues()</code></p>
<div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">class</span> <span class="nc">GeoBoostFunction</span> <span class="kd">extends</span> <span class="n">ValueSource</span> <span class="o">{</span>
<span class="kd">protected</span> <span class="n">ValueSource</span> <span class="n">lat</span><span class="o">,</span> <span class="n">lng</span><span class="o">;</span>
<span class="kd">protected</span> <span class="kt">float</span> <span class="n">latmin</span><span class="o">,</span> <span class="n">lngmin</span><span class="o">,</span> <span class="n">latmax</span><span class="o">,</span> <span class="n">lngmax</span><span class="o">;</span>
<span class="kd">protected</span> <span class="kt">float</span> <span class="n">eps</span><span class="o">,</span> <span class="n">mu</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">GeoBoostFunction</span><span class="o">(</span><span class="kt">float</span> <span class="n">eps</span><span class="o">,</span> <span class="kt">float</span> <span class="n">mu</span><span class="o">,</span> <span class="kt">float</span> <span class="n">latmin</span><span class="o">,</span> <span class="kt">float</span> <span class="n">lngmin</span><span class="o">,</span> <span class="kt">float</span> <span class="n">latmax</span><span class="o">,</span> <span class="kt">float</span> <span class="n">lngmax</span><span class="o">,</span> <span class="n">ValueSource</span> <span class="n">lat</span><span class="o">,</span> <span class="n">ValueSource</span> <span class="n">lng</span><span class="o">)</span> <span class="o">{</span>
<span class="o">...</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">FunctionValues</span> <span class="nf">getValues</span><span class="o">(</span><span class="n">Map</span> <span class="n">context</span><span class="o">,</span> <span class="n">AtomicReaderContext</span> <span class="n">readerContext</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="n">FloatDocValues</span><span class="o">(</span><span class="k">this</span><span class="o">)</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">float</span> <span class="nf">floatVal</span><span class="o">(</span><span class="kt">int</span> <span class="n">doc</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">boost</span><span class="o">(</span><span class="n">doc</span><span class="o">,</span> <span class="n">latvals</span><span class="o">,</span> <span class="n">lngvals</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">...</span>
<span class="o">};</span>
<span class="o">}</span>
<span class="o">...</span>
<span class="o">}</span><span class="c1">//class GeoBoostFunction</span>
</pre></div>
<p>Once the function and parser are written and compiled, we register the function in the sorlconfig.xml by adding the path to our *.jar and specifying our parser.</p>
<div class="highlight"><pre><span></span><span class="nt"><config></span>
...
<span class="nt"><lib</span> <span class="na">path=</span><span class="s">"lib/lbs-geoboost.jar"</span> <span class="nt">/></span>
<span class="nt"><valueSourceParser</span> <span class="na">name=</span><span class="s">"geoboost"</span> <span class="na">class=</span><span class="s">"com.mappy.lbs.solr.search.function.GeoBoostValueParser"</span> <span class="nt">/></span>
...
<span class="nt"></config></span>
</pre></div>
<p>Now we can run and test our configuration.</p>
<p>Using the default bounding box value (0,0,0,0) <code>http://127.0.0.1:8983/solr/acgeo/select?q=pa&wt=json&</code> leads to</p>
<div class="highlight"><pre><span></span><span class="p">{</span><span class="nt">"responseHeader"</span><span class="p">:</span>
<span class="p">{</span><span class="nt">"status"</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="nt">"QTime"</span><span class="p">:</span><span class="mi">21</span><span class="p">},</span>
<span class="nt">"response"</span><span class="p">:</span>
<span class="p">{</span><span class="nt">"numFound"</span><span class="p">:</span><span class="mi">97</span><span class="p">,</span>
<span class="nt">"start"</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span>
<span class="nt">"docs"</span><span class="p">:</span>
<span class="p">[{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Pakistan"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"São Paulo"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Paraguay"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Papua New Guinea"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Palestine"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Panama"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Paris"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Patna"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Palembang"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Padang"</span><span class="p">}</span>
<span class="p">]</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>Using a bounding box around Paris <code>http://127.0.0.1:8983/solr/acgeo/select?q=pa&wt=json&lat_min=48&lat_max=48.5&lng_min=2&lng_max=2.5</code> leads to</p>
<div class="highlight"><pre><span></span><span class="p">{</span><span class="nt">"responseHeader"</span><span class="p">:</span>
<span class="p">{</span><span class="nt">"status"</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="nt">"QTime"</span><span class="p">:</span><span class="mi">8</span><span class="p">},</span>
<span class="nt">"response"</span><span class="p">:</span>
<span class="p">{</span><span class="nt">"numFound"</span><span class="p">:</span><span class="mi">97</span><span class="p">,</span>
<span class="nt">"start"</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span>
<span class="nt">"docs"</span><span class="p">:</span>
<span class="p">[{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Paris"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Sant Andreu de Palomar"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Pakistan"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Palma"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"São Paulo"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Paraguay"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Papua New Guinea"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Palestine"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Panama"</span><span class="p">},</span>
<span class="p">{</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"Patna"</span><span class="p">}</span>
<span class="p">]</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div><!-- /.entry-content -->
</div><!-- /.eleven.columns -->
<div class="four columns" id="sidebar">
<h4>Pages</h4>
<ul>
</ul>
<h4>Categories</h4>
<ul>
<li><a href="./category/agile.html">Agile</a></li>
<li><a href="./category/android.html">Android</a></li>
<li><a href="./category/gis.html">GIS</a></li>
<li><a href="./category/mapping.html">Mapping</a></li>
<li><a href="./category/solr.html">Solr</a></li>
<li><a href="./category/web.html">Web</a></li>
</ul>
<h4>Tags</h4>
<ul>
<li class="tag-4"><a href="./tag/panorama.html">panorama</a></li>
<li class="tag-4"><a href="./tag/responsive.html">responsive</a></li>
<li class="tag-4"><a href="./tag/osm.html">osm</a></li>
<li class="tag-1"><a href="./tag/javascript.html">javascript</a></li>
<li class="tag-4"><a href="./tag/retrospective.html">rétrospective</a></li>
<li class="tag-4"><a href="./tag/gis.html">GIS</a></li>
<li class="tag-4"><a href="./tag/sotm.html">sotm</a></li>
<li class="tag-4"><a href="./tag/android.html">android</a></li>
<li class="tag-4"><a href="./tag/agilite.html">agilité</a></li>
<li class="tag-4"><a href="./tag/webgl.html">webgl</a></li>
<li class="tag-4"><a href="./tag/openlr.html">openlr</a></li>
<li class="tag-3"><a href="./tag/postgis.html">postGIS</a></li>
<li class="tag-3"><a href="./tag/mapnik.html">mapnik</a></li>
<li class="tag-4"><a href="./tag/abtest.html">abtest</a></li>
<li class="tag-3"><a href="./tag/leaflet.html">leaflet</a></li>
<li class="tag-4"><a href="./tag/python.html">python</a></li>
<li class="tag-3"><a href="./tag/backbone.html">backbone</a></li>
<li class="tag-1"><a href="./tag/opensource.html">opensource</a></li>
<li class="tag-1"><a href="./tag/francais.html">français</a></li>
<li class="tag-4"><a href="./tag/watch.html">watch</a></li>
<li class="tag-4"><a href="./tag/meetup.html">meetup</a></li>
<li class="tag-4"><a href="./tag/browserify.html">browserify</a></li>
<li class="tag-4"><a href="./tag/opengl.html">opengl</a></li>
<li class="tag-4"><a href="./tag/docker.html">docker</a></li>
<li class="tag-3"><a href="./tag/solr.html">solr</a></li>
<li class="tag-2"><a href="./tag/nodejs.html">node.js</a></li>
<li class="tag-2"><a href="./tag/webperfs.html">webperfs</a></li>
<li class="tag-2"><a href="./tag/english.html">english</a></li>
<li class="tag-4"><a href="./tag/livereload.html">livereload</a></li>
</ul>
<nav class="widget">
<h4>Links</h4>
<ul>
<li><a href="https://www.mappy.com/">Mappy</a></li>
<li><a href="https://play.google.com/store/apps/details?id=com.mappy.app">Appli Android</a></li>
<li><a href="https://itunes.apple.com/fr/app/mappy-itineraire-et-recherche/id313834655?mt=8">Appli iOS</a></li>
<li><a href="http://corporate.mappy.com">Blog Mappy</a></li>
<li><a href="http://corporate.mappy.com/faq/integrez-mappy/">API Mappy</a></li>
</ul>
</nav>
</div> </div><!-- /.row -->
</section>
</div><!-- /.row -->
</div><!-- /.container -->
<div class="container.nopad bg">
<footer id="credits" class="row">
<div class="seven columns left-center">
<address id="about" class="vcard body">
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
which takes great advantage of <a href="http://python.org">Python</a>.
<br />
Based on the <a target="_blank" href="http://gumbyframework.com">Gumby Framework</a>
</address>
</div>
<div class="seven columns">
<div class="row">
<ul class="socbtns">
<li><div class="btn primary"><a href="https://github.com/Mappy" target="_blank">Github</a></div></li>
<li><div class="btn twitter"><a href="https://twitter.com/Mappy" target="_blank">Twitter</a></div></li>
<li><div class="btn facebook"><a href="https://www.facebook.com/MappyOnline" target="_blank">Facebook</a></div></li>
</ul>
</div>
</div>
</footer>
</div>
</body>
</html>