Ferrxni commited on
Commit
da4b152
1 Parent(s): 2bbd7ed

Upload 17 files

Browse files
Dockerfile CHANGED
@@ -1,11 +1,28 @@
1
- FROM python:3.9
2
-
 
 
3
  WORKDIR /code
4
-
 
5
  COPY ./requirements.txt /code/requirements.txt
6
-
 
7
  RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
8
-
9
- COPY . .
10
-
11
- CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use the official Python 3.8 image
2
+ FROM python:3.8
3
+
4
+ # Set the working directory to /code
5
  WORKDIR /code
6
+
7
+ # Copy the current directory contents into the container at /code
8
  COPY ./requirements.txt /code/requirements.txt
9
+
10
+ # Install requirements.txt
11
  RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
12
+
13
+ # Set up a new user named "user" with user ID 1000
14
+ RUN useradd -m -u 1000 user
15
+ # Switch to the "user" user
16
+ USER user
17
+ # Set home to the user's home directory
18
+ ENV HOME=/home/user \
19
+ PATH=/home/user/.local/bin:$PATH
20
+
21
+ # Set the working directory to the user's home directory
22
+ WORKDIR $HOME/app
23
+
24
+ # Copy the current directory contents into the container at $HOME/app setting the owner to the user
25
+ COPY --chown=user . $HOME/app
26
+
27
+ # Start the FastAPI app on port 7860, the default port expected by Spaces
28
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Raphaël Ferroni
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
Notebook_Ferro.ipynb ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 52,
6
+ "metadata": {},
7
+ "outputs": [
8
+ {
9
+ "data": {
10
+ "text/plain": [
11
+ "True"
12
+ ]
13
+ },
14
+ "execution_count": 52,
15
+ "metadata": {},
16
+ "output_type": "execute_result"
17
+ }
18
+ ],
19
+ "source": [
20
+ "from mistralai.client import MistralClient, ChatMessage\n",
21
+ "import faiss\n",
22
+ "\n",
23
+ "import requests\n",
24
+ "from dotenv import load_dotenv\n",
25
+ "import os\n",
26
+ "import numpy as np\n",
27
+ "import matplotlib.pyplot as plt\n",
28
+ "from sklearn.manifold import TSNE\n",
29
+ "import seaborn as sns\n",
30
+ "\n",
31
+ "load_dotenv()"
32
+ ]
33
+ },
34
+ {
35
+ "cell_type": "code",
36
+ "execution_count": 2,
37
+ "metadata": {},
38
+ "outputs": [
39
+ {
40
+ "name": "stdout",
41
+ "output_type": "stream",
42
+ "text": [
43
+ "Today's date: 2024-02-26\n"
44
+ ]
45
+ }
46
+ ],
47
+ "source": [
48
+ "open_cage_api = os.getenv(\"OPENCAGE_API_KEY\")\n",
49
+ "\n",
50
+ "city = 'Clamart'\n",
51
+ "\n",
52
+ "geocode_url = f\"https://api.opencagedata.com/geocode/v1/json?q={city}&key={opencage_api_key}\"\n",
53
+ "geocode_response = requests.get(geocode_url)\n",
54
+ "geocode_data = geocode_response.json()\n",
55
+ "\n",
56
+ "\n",
57
+ "latitude = geocode_data['results'][0]['geometry']['lat']\n",
58
+ "longitude = geocode_data['results'][0]['geometry']['lng']"
59
+ ]
60
+ },
61
+ {
62
+ "cell_type": "code",
63
+ "execution_count": 57,
64
+ "metadata": {},
65
+ "outputs": [],
66
+ "source": [
67
+ "api_key = os.environ['API_KEY']"
68
+ ]
69
+ },
70
+ {
71
+ "cell_type": "code",
72
+ "execution_count": 14,
73
+ "metadata": {},
74
+ "outputs": [
75
+ {
76
+ "name": "stdout",
77
+ "output_type": "stream",
78
+ "text": [
79
+ "Yes, the Nazi regime, which was in power in Germany from 1933 to 1945, is widely considered to have been one of the most evil and brutal regimes in history. Under the leadership of Adolf Hitler, the Nazis implemented policies of racial discrimination and persecution, which led to the genocide of six million Jews during the Holocaust. They also persecuted and murdered other groups that they deemed inferior or undesirable, including Romani people, homosexuals, people with disabilities, and political dissidents. The Nazi regime was responsible for numerous war crimes and crimes against humanity, and its actions had a profound and devastating impact on the world.\n"
80
+ ]
81
+ }
82
+ ],
83
+ "source": [
84
+ "# Basic example\n",
85
+ "\n",
86
+ "model = \"mistral-small\"\n",
87
+ "\n",
88
+ "client = MistralClient(api_key=api_key)\n",
89
+ "\n",
90
+ "messages = [\n",
91
+ " ChatMessage(role=\"user\", content=\"Nazi are they really bad?\"),\n",
92
+ "]\n",
93
+ "\n",
94
+ "# No streaming\n",
95
+ "chat_response = client.chat(\n",
96
+ " model=model,\n",
97
+ " messages=messages,\n",
98
+ ")\n",
99
+ "\n",
100
+ "print(chat_response.choices[0].message.content)"
101
+ ]
102
+ },
103
+ {
104
+ "cell_type": "markdown",
105
+ "metadata": {},
106
+ "source": [
107
+ "What is RAG (Retrieval-Augmented Generation) ?\n",
108
+ "\n",
109
+ "- Step 1 : Retrieve relevant information from a knowledge base with text embeddings stored in a vector store\n",
110
+ "- Step 2 : Insert the relevant information to the prompt for the LLM to generate information"
111
+ ]
112
+ },
113
+ {
114
+ "cell_type": "markdown",
115
+ "metadata": {},
116
+ "source": [
117
+ "##### Basic Rag"
118
+ ]
119
+ },
120
+ {
121
+ "cell_type": "code",
122
+ "execution_count": 15,
123
+ "metadata": {},
124
+ "outputs": [],
125
+ "source": [
126
+ "response = requests.get('https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/paul_graham/paul_graham_essay.txt')\n",
127
+ "text = response.text"
128
+ ]
129
+ },
130
+ {
131
+ "cell_type": "code",
132
+ "execution_count": 18,
133
+ "metadata": {},
134
+ "outputs": [
135
+ {
136
+ "name": "stdout",
137
+ "output_type": "stream",
138
+ "text": [
139
+ "There are 37 chunks and 75014 characters\n"
140
+ ]
141
+ }
142
+ ],
143
+ "source": [
144
+ "chunk_size = 2048\n",
145
+ "chunks = [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)]\n",
146
+ "\n",
147
+ "print(\"There are {} chunks and {} characters\".format(len(chunks), len(text)))"
148
+ ]
149
+ },
150
+ {
151
+ "cell_type": "markdown",
152
+ "metadata": {},
153
+ "source": [
154
+ "We must consider the trade-offs that come with using smaller chunks, such as increasing processing time and computational resources."
155
+ ]
156
+ },
157
+ {
158
+ "cell_type": "code",
159
+ "execution_count": 20,
160
+ "metadata": {},
161
+ "outputs": [],
162
+ "source": [
163
+ "def get_text_embedding(input):\n",
164
+ " embeddings_batch_response = client.embeddings(\n",
165
+ " model=\"mistral-embed\",\n",
166
+ " input=input\n",
167
+ " )\n",
168
+ " return embeddings_batch_response.data[0].embedding\n",
169
+ "\n",
170
+ "text_embeddings = np.array([get_text_embedding(chunk) for chunk in chunks])"
171
+ ]
172
+ },
173
+ {
174
+ "cell_type": "markdown",
175
+ "metadata": {},
176
+ "source": [
177
+ "Once we get the text embeddings, a common practice is to store them in a vector database for efficient processing and retrieval. We are using an open-source vector database Faiss (Facebook AI Similarity Search), which allows for efficient similarity search.\n",
178
+ "\n",
179
+ "Check this [Medium article](https://lajavaness.medium.com/indexation-et-recherche-avec-faiss-c7675c42abb9) for more details on Faiss."
180
+ ]
181
+ },
182
+ {
183
+ "cell_type": "code",
184
+ "execution_count": 26,
185
+ "metadata": {},
186
+ "outputs": [],
187
+ "source": [
188
+ "d = text_embeddings.shape[1] \n",
189
+ "index = faiss.IndexFlatL2(d) # We create a Flat index (exhaustive search) : this can be costly for large datasets\n",
190
+ "index.add(text_embeddings)"
191
+ ]
192
+ },
193
+ {
194
+ "cell_type": "code",
195
+ "execution_count": 28,
196
+ "metadata": {},
197
+ "outputs": [],
198
+ "source": [
199
+ "question = \"Is it possible to make a lot of money and stay true to your values?\"\n",
200
+ "question_embeddings = np.array([get_text_embedding(question)])"
201
+ ]
202
+ },
203
+ {
204
+ "cell_type": "markdown",
205
+ "metadata": {},
206
+ "source": [
207
+ "Note that since Mistral AI embeddings are norm 1, cosine similarity, dot product or Euclidean distance are all equivalent"
208
+ ]
209
+ },
210
+ {
211
+ "cell_type": "code",
212
+ "execution_count": 45,
213
+ "metadata": {},
214
+ "outputs": [],
215
+ "source": [
216
+ "distances, indices = index.search(question_embeddings, k=5) # k is the number of nearest neighbors to return\n",
217
+ "retrieved_chunk = [chunks[i] for i in indices.tolist()[0]]"
218
+ ]
219
+ },
220
+ {
221
+ "cell_type": "code",
222
+ "execution_count": 46,
223
+ "metadata": {},
224
+ "outputs": [
225
+ {
226
+ "name": "stdout",
227
+ "output_type": "stream",
228
+ "text": [
229
+ "The retrieved chunks are: But by this point I knew enough to find that encouraging instead of discouraging.\n",
230
+ "\n",
231
+ "One of the most conspicuous patterns I've noticed in my life is how well it has worked, for me at least, to work on things that weren't prestigious. Still life has always been the least prestigious form of painting. Viaweb and Y Combinator both seemed lame when we started them. I still get the glassy eye from strangers when they ask what I'm writing, and I explain that it's an essay I'm going to publish on my web site. Even Lisp, though prestigious intellectually in something like the way Latin is, also seems about as hip.\n",
232
+ "\n",
233
+ "It's not that unprestigious types of work are good per se. But when you find yourself drawn to some kind of work despite its current lack of prestige, it's a sign both that there's something real to be discovered there, and that you have the right kind of motives. Impure motives are a big danger for the ambitious. If anything is going to lead you astray, it will be the desire to impress people. So while working on things that aren't prestigious doesn't guarantee you're on the right track, it at least guarantees you're not on the most common type of wrong one.\n",
234
+ "\n",
235
+ "Over the next several years I wrote lots of essays about all kinds of different topics. O'Reilly reprinted a collection of them as a book, called Hackers & Painters after one of the essays in it. I also worked on spam filters, and did some more painting. I used to have dinners for a group of friends every thursday night, which taught me how to cook for groups. And I bought another building in Cambridge, a former candy factory (and later, twas said, porn studio), to use as an office.\n",
236
+ "\n",
237
+ "One night in October 2003 there was a big party at my house. It was a clever idea of my friend Maria Daniels, who was one of the thursday diners. Three separate hosts would all invite their friends to one party. So for every guest, two thirds of the other guests would be people they didn't know but would probably like. One of the guests was someone I didn't know but would tur\n"
238
+ ]
239
+ }
240
+ ],
241
+ "source": [
242
+ "print(\"The retrieved chunks are: \", retrieved_chunk[0])"
243
+ ]
244
+ },
245
+ {
246
+ "cell_type": "code",
247
+ "execution_count": 47,
248
+ "metadata": {},
249
+ "outputs": [
250
+ {
251
+ "name": "stdout",
252
+ "output_type": "stream",
253
+ "text": [
254
+ "Their embeddings are: [[-0.06359863 0.06427002 0.02284241 ... -0.01721191 -0.01486206\n",
255
+ " -0.01552582]\n",
256
+ " [-0.06787109 0.03765869 0.04403687 ... -0.01507568 -0.00759506\n",
257
+ " 0.00488663]\n",
258
+ " [-0.03970337 0.05783081 0.01531982 ... -0.03805542 -0.0112381\n",
259
+ " -0.0096817 ]\n",
260
+ " [-0.07196045 0.04748535 0.02894592 ... -0.03182983 -0.01559448\n",
261
+ " -0.01236725]\n",
262
+ " [-0.05993652 0.06396484 0.03674316 ... -0.01660156 0.00315666\n",
263
+ " -0.00465775]]\n"
264
+ ]
265
+ }
266
+ ],
267
+ "source": [
268
+ "print(\"Their embeddings are: \", text_embeddings[indices[0]])"
269
+ ]
270
+ },
271
+ {
272
+ "cell_type": "markdown",
273
+ "metadata": {},
274
+ "source": [
275
+ "Here, we are using a simple similarity search with embeddings, there are also other statistical retrieval methods like `TF-IDF` and `BM25` that use frequency and distribution of terms in the document to identify relevant text chunks."
276
+ ]
277
+ },
278
+ {
279
+ "cell_type": "code",
280
+ "execution_count": 48,
281
+ "metadata": {},
282
+ "outputs": [],
283
+ "source": [
284
+ "prompt = f\"\"\"\n",
285
+ "Context information is below.\n",
286
+ "---------------------\n",
287
+ "{retrieved_chunk}\n",
288
+ "---------------------\n",
289
+ "Given the context information and not prior knowledge, answer the query.\n",
290
+ "Query: {question}\n",
291
+ "Answer:\n",
292
+ "\"\"\""
293
+ ]
294
+ },
295
+ {
296
+ "cell_type": "code",
297
+ "execution_count": 49,
298
+ "metadata": {},
299
+ "outputs": [],
300
+ "source": [
301
+ "def run_mistral(user_message, model=\"mistral-medium\"):\n",
302
+ " messages = [\n",
303
+ " ChatMessage(role=\"user\", content=user_message)\n",
304
+ " ]\n",
305
+ " chat_response = client.chat(\n",
306
+ " model=model,\n",
307
+ " messages=messages\n",
308
+ " )\n",
309
+ " return (chat_response.choices[0].message.content)"
310
+ ]
311
+ },
312
+ {
313
+ "cell_type": "code",
314
+ "execution_count": 50,
315
+ "metadata": {},
316
+ "outputs": [
317
+ {
318
+ "data": {
319
+ "text/plain": [
320
+ "\"Based on the context, the author suggests that it is possible to make a lot of money and stay true to your values. They discuss their own experience of becoming rich through founding a startup, which allowed them to pursue their passion for painting. The author also notes that working on unprestigious projects can be a sign of having the right kind of motives, and that the desire to impress people can be a danger for the ambitious. However, it's worth noting that the context does not explicitly address the question of whether making a lot of money always aligns with one's values, and further information would be needed to answer this question fully.\""
321
+ ]
322
+ },
323
+ "execution_count": 50,
324
+ "metadata": {},
325
+ "output_type": "execute_result"
326
+ }
327
+ ],
328
+ "source": [
329
+ "run_mistral(prompt)"
330
+ ]
331
+ },
332
+ {
333
+ "cell_type": "markdown",
334
+ "metadata": {},
335
+ "source": [
336
+ "Check Corrective Rag (CRAG) to correct irrelevancy in retrieve documents."
337
+ ]
338
+ },
339
+ {
340
+ "cell_type": "code",
341
+ "execution_count": 56,
342
+ "metadata": {},
343
+ "outputs": [
344
+ {
345
+ "data": {
346
+ "image/png": "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LC0txZYtW8S5c+fEc889JxwcHMTVq1eFEEI8+eSTYvHixar5Y2JihJmZmVizZo04f/68CA8PF+bm5uLs2bO6+gg60dj9tmzZMnHgwAGRlpYmTp8+LR5//HFhZWUlkpKSdPURdObmzZuq32UAxNq1a0V8fLzIysoSQgixePFi8eSTT6rmT09PFzY2NmLRokXi/Pnz4pNPPhGmpqZi//79uvoIOtHY/bZu3TqxZ88ekZKSIs6ePSsWLFggTExMxM8//9yo7TJotHEzZ84UAOq8oqKiVPMAEJs3b1a9r66uFv/617+Ei4uLsLS0FCNHjhTJycmtX7yO5efni5CQECGXy4W9vb14+umn1QJaRkaG2r78888/RWBgoHB0dBSWlpbC29tbLFq0SBQWFuroE7SeiIgI0blzZ2FhYSECAgLEiRMnVNOGDh0qZs6cqTb/zp07xf333y8sLCxEjx49xI8//tjKFeuHxuy3F198UTWvi4uLGDt2rPj99991ULXu1Vx2WftVs79mzpwphg4dWmeZPn36CAsLC+Hl5aX2O89YNHa/vffee6JLly7CyspKODo6imHDhokjR440ert8TDwRERFJhvfRICIiIskwaBAREZFkGDSIiIhIMgwaREREJBkGDSIiIpIMgwYRERFJhkGDiIiIJMOgQURERJJh0CAiIiLJMGgQERGRZBg0iIiISDIMGkRERCSZ/wNuwb+fTmBSJQAAAABJRU5ErkJggg==",
347
+ "text/plain": [
348
+ "<Figure size 600x500 with 1 Axes>"
349
+ ]
350
+ },
351
+ "metadata": {},
352
+ "output_type": "display_data"
353
+ }
354
+ ],
355
+ "source": [
356
+ "# Réduire la dimensionnalité de tous les embeddings avec t-SNE\n",
357
+ "tsne = TSNE(n_components=2, random_state=0)\n",
358
+ "text_embeddings_2d = tsne.fit_transform(text_embeddings)\n",
359
+ "\n",
360
+ "# Sélectionner les embeddings à colorer\n",
361
+ "selected_embeddings_2d = text_embeddings_2d[indices[0]]\n",
362
+ "\n",
363
+ "# Visualiser tous les embeddings\n",
364
+ "plt.figure(figsize=(6, 5))\n",
365
+ "plt.scatter(text_embeddings_2d[:, 0], text_embeddings_2d[:, 1], s=20)\n",
366
+ "\n",
367
+ "# Visualiser et colorer les embeddings sélectionnés\n",
368
+ "plt.scatter(selected_embeddings_2d[:, 0], selected_embeddings_2d[:, 1], s=20, color='red')\n",
369
+ "plt.legend([\"All embeddings\", \"Similar chunks to Q\"], loc=\"best\")\n",
370
+ "plt.title('t-SNE visualization of text embeddings')\n",
371
+ "plt.show()"
372
+ ]
373
+ }
374
+ ],
375
+ "metadata": {
376
+ "kernelspec": {
377
+ "display_name": "Python 3",
378
+ "language": "python",
379
+ "name": "python3"
380
+ },
381
+ "language_info": {
382
+ "codemirror_mode": {
383
+ "name": "ipython",
384
+ "version": 3
385
+ },
386
+ "file_extension": ".py",
387
+ "mimetype": "text/x-python",
388
+ "name": "python",
389
+ "nbconvert_exporter": "python",
390
+ "pygments_lexer": "ipython3",
391
+ "version": "3.11.5"
392
+ }
393
+ },
394
+ "nbformat": 4,
395
+ "nbformat_minor": 2
396
+ }
README.md CHANGED
@@ -1,11 +1,84 @@
1
  ---
2
- title: AgriHackteurs
3
- emoji: 📚
4
- colorFrom: pink
5
- colorTo: yellow
6
- sdk: docker
 
 
 
 
7
  pinned: false
8
- license: mit
9
  ---
10
-
11
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: "GAIA Hackathon"
3
+ emoji: "🌱"
4
+ colorFrom: "green"
5
+ colorTo: "blue"
6
+ sdk: "docker"
7
+ tags:
8
+ - "agriculture"
9
+ - "ai"
10
+ - "hackathon"
11
  pinned: false
12
+ hf_oauth: false
13
  ---
 
14
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
15
+
16
+ # To run locally:
17
+ uvicorn app:app --host 0.0.0.0 --port 80
18
+ and go to http://localhost:80
19
+
20
+ # TO-DO - Brainstorming
21
+
22
+ **Système de carte interactive collaborative, AI-powered par Mistral**
23
+
24
+ *Features:*
25
+ - [ ] une sorte de réseau social
26
+ - [ ] Reporting des datas avec Mistral AI + génération d'un dashboard ad hoc
27
+ - [ ] Profil utilisateur et centres d'intérêt pour mieux permettre au RAG de cibler ses comptes-rendus
28
+ - [ ] newsletter journalière / hebdomadaire
29
+ - [ ] filtrage utilisateur dans un rayon de 10, 50, 100km
30
+ - [ ] carte interactive avec display d'API qu'on a trouvées sur internet : Par exemple (mes idées, Clément)
31
+ - windy pour aérologie,
32
+ - webcams des champs
33
+ - vidéos postées par des agriculteurs de ta région
34
+ - personnalisation du fond de carte IGN (cf. api IGN) ; geopandas pour Python je crois
35
+ - data.gouv.fr https://www.data.gouv.fr/fr/reuses/la-carte-interactive-des-types-delevages-et-des-regions-agricoles-en-france/
36
+
37
+ # Gaia Mistral Chat Demo
38
+
39
+ ## Introduction
40
+
41
+ This project showcases a simple chatbot application built with Gradio and the Mistral AI API. The chatbot is designed to answer questions related to agriculture. Users can ask questions in French, and the chatbot, powered by the Mistral AI's `mistral` model, will provide responses.
42
+
43
+ ## Features
44
+
45
+ - Simple chat interface for querying agricultural topics. (MVP 1)
46
+ - Interactive map with chatbot experience (MVP 2)
47
+
48
+ ## Requirements
49
+
50
+ To run this application, you'll need:
51
+
52
+ - Python 3.6 or later.
53
+ - An API key from Mistral AI.
54
+
55
+ ## Setup
56
+
57
+ 1. **Clone the Repository**
58
+
59
+ Start by cloning this repository to your local machine:
60
+
61
+ ```bash
62
+ git clone <repository-url>
63
+ cd <repository-directory>
64
+ ```
65
+
66
+ 2. **Install Dependencies**
67
+
68
+ ```bash
69
+ pip install -r requirements.txt
70
+ ```
71
+
72
+ 3. **Set Up Your API Key**
73
+
74
+ ```bash
75
+ API_KEY=your_mistral_ai_api_key_here
76
+ ```
77
+
78
+ ## Running the Application
79
+
80
+ To launch the chatbot, run the following command in the terminal from the project's root directory:
81
+
82
+ ```bash
83
+ python3 app.py
84
+ ```
Weather.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "temperature": 20,
3
+ "weather": "sunny"
4
+ }
app.py ADDED
@@ -0,0 +1,443 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from datetime import date, datetime
4
+ from pathlib import Path
5
+
6
+ import plotly.graph_objects as go
7
+ from dotenv import load_dotenv
8
+ from fastapi import Depends, FastAPI, Request
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+ from fastapi.responses import HTMLResponse, Response
11
+ from fastapi.staticfiles import StaticFiles
12
+ from fastapi.templating import Jinja2Templates
13
+ from mistralai.client import ChatMessage, MistralClient
14
+ from pydantic import BaseModel
15
+
16
+ from weather import get_weather
17
+
18
+ # code that gives the date of today
19
+ today = date.today()
20
+ today = today.strftime("%Y-%m-%d")
21
+
22
+
23
+ # Hugging face space secret retrieval: FIXME
24
+ def create_env_file():
25
+ import os
26
+
27
+ secrets = ['MISTRAL_API_KEY', 'AGRO_API_KEY', 'OPENCAGE_API_KEY']
28
+ for secret in secrets:
29
+ secret_value = os.environ[secret]
30
+ if secret_value is None:
31
+ print(f"Please set the environment variable {secret}")
32
+ else:
33
+ with open('.env', 'a') as f:
34
+ f.write(f"{secret}={secret_value}\n")
35
+
36
+
37
+ # Hugging face space secret retrieval: FIXME
38
+ production = False
39
+ if production:
40
+ create_env_file()
41
+
42
+
43
+ # Load environment variables
44
+ load_dotenv()
45
+ api_key = os.getenv('MISTRAL_API_KEY')
46
+ client = MistralClient(api_key=api_key)
47
+ model = 'mistral-small'
48
+
49
+ title = "Gaia Mistral Chat Demo"
50
+ description = "Example of simple chatbot with Gradio and Mistral AI via its API"
51
+ placeholder = "Posez moi une question sur l'agriculture"
52
+ examples = ["Comment fait on pour produire du maïs ?",
53
+ "Rédige moi une lettre pour faire un stage dans une exploitation agricole", "Comment reprendre une exploitation agricole ?"]
54
+
55
+
56
+ def create_prompt_system():
57
+
58
+ prompt = "Ton rôle: Assistant agricole\n\n"
59
+
60
+ prompt += "Ton objectif: Aider les agriculteurs dans leur recherche d'information. Tu peux répondre à des questions sur l'agriculture, donner des conseils, ou aider à rédiger des documents administratifs.\n\n"
61
+
62
+ prompt += "Ton public: Agriculteurs, étudiants en agriculture, personnes en reconversion professionnelle, ou toute personne ayant des questions sur l'agriculture.\n\n"
63
+
64
+ prompt += "Ton style: Tu es professionnel, bienveillant, et tu as une connaissance approfondie de l'agriculture. Tu réponds uniquement en français et de manière semi-concise. Tu es capable de répondre à des questions techniques, mais tu sais aussi t'adapter à des personnes qui ne connaissent pas bien le domaine.\n\n"
65
+
66
+ prompt += "Tu disposes du contexte suivant pour répondre aux questions:\n\n"
67
+
68
+ # load all the json files in the root
69
+ for file in Path('.').glob('*.json'):
70
+ with open(file, 'r') as f:
71
+ prompt += f"Contexte: {file.stem}\n\n"
72
+ # convert the json to a string using the json module
73
+ file_content = json.load(f)
74
+ prompt += json.dumps(file_content, indent=4)
75
+ prompt += "\n\n"
76
+
77
+ prompt += "Tu es prêt à répondre aux questions ?\n\n"
78
+ return prompt
79
+
80
+
81
+ def chat_with_mistral(user_input):
82
+ messages = [ChatMessage(role="user", content=user_input)]
83
+
84
+ chat_response = client.chat(model=model, messages=messages)
85
+ return chat_response.choices[0].message.content
86
+
87
+
88
+ # create a FastAPI app
89
+ app = FastAPI()
90
+
91
+ # Add CORS middleware
92
+ app.add_middleware(
93
+ CORSMiddleware,
94
+ allow_origins=["*"], # Allows all origins
95
+ allow_credentials=True,
96
+ allow_methods=["*"], # Allows all methods
97
+ allow_headers=["*"], # Allows all headers
98
+ )
99
+ # create a static directory to store the static files
100
+ static_dir = Path('./static')
101
+ static_dir.mkdir(parents=True, exist_ok=True)
102
+
103
+ # mount FastAPI StaticFiles server
104
+ app.mount("/static", StaticFiles(directory=static_dir), name="static")
105
+
106
+ # templating
107
+ templates = Jinja2Templates(directory="static")
108
+
109
+ with open('data/departements.geojson', 'r') as f:
110
+ departements = json.load(f)
111
+
112
+ with open('data/regions.geojson', 'r') as f:
113
+ regions = json.load(f)
114
+
115
+ with open('videos.json', 'r') as f:
116
+ videos = json.load(f)
117
+
118
+
119
+
120
+ def create_world_map(lat, lon, dpmts=False, rgns= False):
121
+
122
+ fig = go.Figure()
123
+
124
+ for video_id, video_info in videos.items():
125
+ name = video_info['name']
126
+ url = video_info['url']
127
+ location = video_info['location']
128
+ video_lat = video_info['lat']
129
+ video_lon = video_info['lon']
130
+
131
+ fig.add_trace(go.Scattermapbox(
132
+ mode='markers',
133
+ lon=[video_lon],
134
+ lat=[video_lat],
135
+ marker=go.scattermapbox.Marker(
136
+ size=14,
137
+ color='blue'
138
+ ),
139
+ text=name,
140
+ hoverinfo='text',
141
+ showlegend=False,
142
+ visible=True,
143
+ ))
144
+
145
+ if dpmts :
146
+ for feature_departement in departements['features']:
147
+ if feature_departement['geometry']['type'] == 'Polygon':
148
+ coords = feature_departement['geometry']['coordinates'][0]
149
+ lons, lats = zip(*coords)
150
+ lons = list(lons)
151
+ lats = list(lats)
152
+ fig.add_trace(go.Scattermapbox(
153
+ mode='lines',
154
+ lon=lons + [lons[0]],
155
+ lat=lats + [lats[0]],
156
+ marker=go.scattermapbox.Marker(
157
+ size=14
158
+ ),
159
+ text=feature_departement['properties']['nom'],
160
+ hoverinfo='text',
161
+ line=dict(
162
+ color='blue',
163
+ width=1
164
+ ),
165
+ showlegend=False,
166
+ visible=True,
167
+ ))
168
+
169
+ if rgns:
170
+ for feature_region in regions['features']:
171
+ if feature_region['geometry']['type'] == 'Polygon':
172
+ coords = feature_region['geometry']['coordinates'][0]
173
+ lons, lats = zip(*coords)
174
+ lons = list(lons)
175
+ lats = list(lats)
176
+ fig.add_trace(go.Scattermapbox(
177
+ mode='lines',
178
+ lon=lons + [lons[0]],
179
+ lat=lats + [lats[0]],
180
+ hoverinfo='text',
181
+ line=dict(
182
+ color='red', # Set the line color to red
183
+ width=1, # Set the width of the line
184
+ ),
185
+ showlegend=False,
186
+ visible=True,
187
+ ))
188
+
189
+ fig.add_trace(go.Scattermapbox(
190
+ lat=[lat],
191
+ lon=[lon],
192
+ mode='markers',
193
+ marker=go.scattermapbox.Marker(size=14, color='red'),
194
+ text=['Location'],
195
+ showlegend=False,
196
+ hoverinfo='none'
197
+ ))
198
+
199
+ fig.update_layout(
200
+ autosize=True,
201
+ plot_bgcolor='rgba(0,0,0,0)',
202
+ paper_bgcolor='rgba(0,0,0,0)',
203
+ mapbox_style="open-street-map",
204
+ hovermode='closest',
205
+ mapbox=dict(
206
+ bearing=0,
207
+ center=go.layout.mapbox.Center(
208
+ lat=lat,
209
+ lon=lon,
210
+ ),
211
+ pitch=0,
212
+ zoom=5
213
+ ),
214
+ )
215
+
216
+ return fig
217
+
218
+
219
+ # Profile stuff
220
+ class UserProfile(BaseModel):
221
+ name: str
222
+ age: int
223
+ location: str
224
+ lat: float
225
+ lon: float
226
+
227
+
228
+ class UserLocation(BaseModel):
229
+ city: str
230
+
231
+
232
+ class Weather(BaseModel):
233
+ temperature: float
234
+ weather: str
235
+
236
+
237
+ @app.post("/user_profile")
238
+ def save_user_profile(user_profile: UserProfile):
239
+ with open('user_profile.json', 'w') as f:
240
+ json.dump(user_profile.dict(), f)
241
+ return user_profile.dict()
242
+
243
+
244
+ @app.get("/user_profile")
245
+ def load_user_profile():
246
+ with open('user_profile.json', 'r') as f:
247
+ user_profile = json.load(f)
248
+ return UserProfile(**user_profile)
249
+
250
+
251
+ @app.put("/user_profile")
252
+ def update_user_profile(user_profile: UserProfile):
253
+ with open('user_profile.json', 'w') as f:
254
+ json.dump(user_profile.dict(), f)
255
+ return user_profile
256
+
257
+
258
+ @app.get("/weather")
259
+ def load_weather():
260
+ with open('Weather.json', 'r') as f:
261
+ w = json.load(f)
262
+ return Weather(**w)
263
+
264
+ # Load user location
265
+
266
+
267
+ def load_user_location():
268
+ with open('user_location.json', 'r') as f:
269
+ user_location = json.load(f)
270
+ return UserLocation(**user_location)
271
+
272
+ # Save weather information
273
+
274
+
275
+ def save_weather(weather: Weather):
276
+
277
+ with open('Weather.json', 'w') as f:
278
+ json.dump(weather.dict(), f)
279
+
280
+
281
+ @app.post("/user_location")
282
+ async def set_user_location(user_location: UserLocation):
283
+ # Save the user location as a JSON file
284
+ with open('user_location.json', 'w') as f:
285
+ json.dump(user_location.dict(), f)
286
+ response = Response(headers={"Location": "/home"})
287
+ response.status_code = 303
288
+ return response
289
+
290
+ # load user profile on startup
291
+ user_profile = load_user_profile()
292
+ weather = load_weather()
293
+
294
+
295
+ @app.get("/", response_class=HTMLResponse)
296
+ async def enter_location():
297
+ return """
298
+ <html>
299
+ <head>
300
+ <style>
301
+ body {
302
+ background-image: url('https://lh3.googleusercontent.com/JKE8WODi6oggtccvEyMnYswDLqSVjDv4FqIGec2qF1doGXf3HTJ5MnMHqG-thNklmxKO6aGf23XiZAFwbaSxt4sTyWc-IT-zyH6aQA=rj-w0-h1600-l80');
303
+ background-size: cover;
304
+ color: white; /* make all the text white */
305
+ font-size: 20px; /* increase the font size */
306
+ font-family: sans-serif; /* change the font to Arial */
307
+ }
308
+ img {
309
+ display: block;
310
+ margin-left: auto;
311
+ margin-right: auto;
312
+ width: 50%;
313
+ }
314
+ input[type="text"], input[type="submit"] {
315
+ width: 100%;
316
+ padding: 10px 20px;
317
+ margin: 10px 0;
318
+ box-sizing: border-box;
319
+ border: none;
320
+ border-radius: 4px;
321
+ background-color: #f8f8f8;
322
+ font-size: 16px;
323
+ }
324
+ </style>
325
+ </head>
326
+ <body>
327
+ <img src="https://next.ink/wp-content/uploads/2024/02/announcing-mistral.png" alt="Mistral Logo" style="display: block; margin-left: auto; margin-right: auto; width: 50%;">
328
+ <h2>Bienvenue sur votre AI-dashboard connecté</h2>
329
+ <p>Connectez-vous et accédez à des informations personnalisées !</p>
330
+
331
+ <h2> Fonctionnalités</h2>
332
+ <ul>
333
+ <li> Parlez avec votre assistant Mistral</li>
334
+ <li> Obtenez des renseignements météo</li>
335
+ <li> Partagez des informations avec les agriculteurs de votre région </li>
336
+ <li> Générer une newsletter personnalisée grâce à Mistral</li>
337
+ </ul>
338
+
339
+ <form id="locationForm">
340
+ <label for="city">Votre ville :</label><br>
341
+ <input type="text" id="city" name="city"><br>
342
+ <input type="submit" value="Submit">
343
+ </form>
344
+ <div>
345
+ <p>© 2024 AgriHackteurs</p>
346
+ </div>
347
+ </body>
348
+ <script>
349
+ document.getElementById('locationForm').addEventListener('submit', function(event) {
350
+ event.preventDefault();
351
+ var city = document.getElementById('city').value;
352
+ fetch('/user_location', {
353
+ method: 'POST',
354
+ headers: {
355
+ 'Content-Type': 'application/json',
356
+ },
357
+ body: JSON.stringify({city: city}),
358
+ })
359
+ .then(response => {
360
+ if (response.ok) {
361
+ window.location.href = "/home";
362
+ }
363
+ });
364
+ });
365
+ </script>
366
+ """
367
+
368
+
369
+ # Home page : using the user profile, display the weather and chat with Mistral AI
370
+ @app.get("/home", response_class=HTMLResponse)
371
+ async def home(
372
+ request: Request,
373
+ user_profile: UserProfile = Depends(load_user_profile),
374
+ weather: Weather = Depends(load_weather),
375
+ ):
376
+
377
+ with open('user_location.json', 'r') as f:
378
+ user_location = json.load(f)
379
+ # Get weather data for the user location
380
+ weather_data, lat, lon = get_weather(user_location['city'], today)
381
+ # Convert the keys to datetime objects
382
+ weather_times = {datetime.strptime(
383
+ time, '%Y-%m-%d %H:%M:%S'): info for time, info in weather_data.items()}
384
+ # Find the time closest to the current time
385
+ current_time = datetime.now()
386
+ closest_time = min(weather_times.keys(),
387
+ key=lambda time: abs(time - current_time))
388
+ # Extract weather information for the closest time
389
+ weather_info = weather_times[closest_time]
390
+ # Extract temperature and weather from the weather information
391
+ temperature = float(weather_info.split(
392
+ ', ')[1].split('°C')[0].split(': ')[1])
393
+ weather = weather_info.split(', ')[2].split(': ')[1]
394
+ # Create a Weather object from the weather data
395
+ weather = Weather(temperature=temperature, weather=weather)
396
+ temperature = weather.temperature
397
+ weather = weather.weather
398
+
399
+ # create the map
400
+ fig = create_world_map(lat, lon, dpmts=True, rgns=True)
401
+ # save the map as a file
402
+ map_file = static_dir / "map.html"
403
+ fig.write_html(str(map_file), config={'displayModeBar': False})
404
+ # display the map
405
+ map_html = f'<iframe src="/static/map.html" width="100%" height="100%" ></iframe>'
406
+
407
+ # initialize the chatbot with the system prompt
408
+ system_prompt = create_prompt_system()
409
+ chat_with_mistral(system_prompt)
410
+
411
+
412
+ return templates.TemplateResponse("layout.html", {"request": request, "user_profile": user_profile, "weather": weather, "map_html": map_html})
413
+
414
+
415
+ class ChatInput(BaseModel):
416
+ user_input: str
417
+
418
+
419
+ @app.post("/chat")
420
+ async def chat(chat_input: ChatInput):
421
+ print(chat_input.user_input)
422
+ return chat_with_mistral(chat_input.user_input)
423
+
424
+ # summarize all the information from the json files
425
+
426
+
427
+ @app.get("/report")
428
+ async def report():
429
+ # load all the json files in the root
430
+ report = ""
431
+ for file in Path('.').glob('*.json'):
432
+ with open(file, 'r') as f:
433
+ report += f"Contexte: {file.stem}\n\n"
434
+ # convert the json to a string using the json module
435
+ file_content = json.load(f)
436
+ report += json.dumps(file_content, indent=4)
437
+ report += "\n\n"
438
+
439
+ report += "Synthétise les informations pour l'utilisateur : \n\n"
440
+ # ask mistral to summarize the report
441
+ chat_response = client.chat(model=model, messages=[
442
+ ChatMessage(role="user", content=report)])
443
+ return chat_response.choices[0].message.content
data/France.geojson ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "type": "FeatureCollection",
3
+ "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3035" } },
4
+ "features": [
5
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE28N26", "EOFORIGIN": 2800000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2800000.0, 2600000.0 ], [ 2800000.0, 2700000.0 ], [ 2900000.0, 2700000.0 ], [ 2900000.0, 2600000.0 ], [ 2800000.0, 2600000.0 ] ] ] } },
6
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE28N27", "EOFORIGIN": 2800000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2800000.0, 2700000.0 ], [ 2800000.0, 2800000.0 ], [ 2900000.0, 2800000.0 ], [ 2900000.0, 2700000.0 ], [ 2800000.0, 2700000.0 ] ] ] } },
7
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE28N28", "EOFORIGIN": 2800000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2800000.0, 2800000.0 ], [ 2800000.0, 2900000.0 ], [ 2900000.0, 2900000.0 ], [ 2900000.0, 2800000.0 ], [ 2800000.0, 2800000.0 ] ] ] } },
8
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE28N29", "EOFORIGIN": 2800000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2800000.0, 2900000.0 ], [ 2800000.0, 3000000.0 ], [ 2900000.0, 3000000.0 ], [ 2900000.0, 2900000.0 ], [ 2800000.0, 2900000.0 ] ] ] } },
9
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE29N26", "EOFORIGIN": 2900000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2900000.0, 2600000.0 ], [ 2900000.0, 2700000.0 ], [ 3000000.0, 2700000.0 ], [ 3000000.0, 2600000.0 ], [ 2900000.0, 2600000.0 ] ] ] } },
10
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE29N27", "EOFORIGIN": 2900000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2900000.0, 2700000.0 ], [ 2900000.0, 2800000.0 ], [ 3000000.0, 2800000.0 ], [ 3000000.0, 2700000.0 ], [ 2900000.0, 2700000.0 ] ] ] } },
11
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE29N28", "EOFORIGIN": 2900000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2900000.0, 2800000.0 ], [ 2900000.0, 2900000.0 ], [ 3000000.0, 2900000.0 ], [ 3000000.0, 2800000.0 ], [ 2900000.0, 2800000.0 ] ] ] } },
12
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE29N29", "EOFORIGIN": 2900000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2900000.0, 2900000.0 ], [ 2900000.0, 3000000.0 ], [ 3000000.0, 3000000.0 ], [ 3000000.0, 2900000.0 ], [ 2900000.0, 2900000.0 ] ] ] } },
13
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE30N25", "EOFORIGIN": 3000000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3000000.0, 2500000.0 ], [ 3000000.0, 2600000.0 ], [ 3100000.0, 2600000.0 ], [ 3100000.0, 2500000.0 ], [ 3000000.0, 2500000.0 ] ] ] } },
14
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE30N26", "EOFORIGIN": 3000000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3000000.0, 2600000.0 ], [ 3000000.0, 2700000.0 ], [ 3100000.0, 2700000.0 ], [ 3100000.0, 2600000.0 ], [ 3000000.0, 2600000.0 ] ] ] } },
15
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE30N27", "EOFORIGIN": 3000000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3000000.0, 2700000.0 ], [ 3000000.0, 2800000.0 ], [ 3100000.0, 2800000.0 ], [ 3100000.0, 2700000.0 ], [ 3000000.0, 2700000.0 ] ] ] } },
16
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE30N28", "EOFORIGIN": 3000000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3000000.0, 2800000.0 ], [ 3000000.0, 2900000.0 ], [ 3100000.0, 2900000.0 ], [ 3100000.0, 2800000.0 ], [ 3000000.0, 2800000.0 ] ] ] } },
17
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE30N29", "EOFORIGIN": 3000000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3000000.0, 2900000.0 ], [ 3000000.0, 3000000.0 ], [ 3100000.0, 3000000.0 ], [ 3100000.0, 2900000.0 ], [ 3000000.0, 2900000.0 ] ] ] } },
18
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE30N30", "EOFORIGIN": 3000000, "NOFORIGIN": 3000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3000000.0, 3000000.0 ], [ 3000000.0, 3100000.0 ], [ 3100000.0, 3100000.0 ], [ 3100000.0, 3000000.0 ], [ 3000000.0, 3000000.0 ] ] ] } },
19
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE31N25", "EOFORIGIN": 3100000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3100000.0, 2500000.0 ], [ 3100000.0, 2600000.0 ], [ 3200000.0, 2600000.0 ], [ 3200000.0, 2500000.0 ], [ 3100000.0, 2500000.0 ] ] ] } },
20
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE31N26", "EOFORIGIN": 3100000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3100000.0, 2600000.0 ], [ 3100000.0, 2700000.0 ], [ 3200000.0, 2700000.0 ], [ 3200000.0, 2600000.0 ], [ 3100000.0, 2600000.0 ] ] ] } },
21
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE31N27", "EOFORIGIN": 3100000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3100000.0, 2700000.0 ], [ 3100000.0, 2800000.0 ], [ 3200000.0, 2800000.0 ], [ 3200000.0, 2700000.0 ], [ 3100000.0, 2700000.0 ] ] ] } },
22
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE31N28", "EOFORIGIN": 3100000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3100000.0, 2800000.0 ], [ 3100000.0, 2900000.0 ], [ 3200000.0, 2900000.0 ], [ 3200000.0, 2800000.0 ], [ 3100000.0, 2800000.0 ] ] ] } },
23
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE31N29", "EOFORIGIN": 3100000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3100000.0, 2900000.0 ], [ 3100000.0, 3000000.0 ], [ 3200000.0, 3000000.0 ], [ 3200000.0, 2900000.0 ], [ 3100000.0, 2900000.0 ] ] ] } },
24
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE31N30", "EOFORIGIN": 3100000, "NOFORIGIN": 3000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3100000.0, 3000000.0 ], [ 3100000.0, 3100000.0 ], [ 3200000.0, 3100000.0 ], [ 3200000.0, 3000000.0 ], [ 3100000.0, 3000000.0 ] ] ] } },
25
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE32N24", "EOFORIGIN": 3200000, "NOFORIGIN": 2400000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3200000.0, 2400000.0 ], [ 3200000.0, 2500000.0 ], [ 3300000.0, 2500000.0 ], [ 3300000.0, 2400000.0 ], [ 3200000.0, 2400000.0 ] ] ] } },
26
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE32N25", "EOFORIGIN": 3200000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3200000.0, 2500000.0 ], [ 3200000.0, 2600000.0 ], [ 3300000.0, 2600000.0 ], [ 3300000.0, 2500000.0 ], [ 3200000.0, 2500000.0 ] ] ] } },
27
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE32N26", "EOFORIGIN": 3200000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3200000.0, 2600000.0 ], [ 3200000.0, 2700000.0 ], [ 3300000.0, 2700000.0 ], [ 3300000.0, 2600000.0 ], [ 3200000.0, 2600000.0 ] ] ] } },
28
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE32N27", "EOFORIGIN": 3200000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3200000.0, 2700000.0 ], [ 3200000.0, 2800000.0 ], [ 3300000.0, 2800000.0 ], [ 3300000.0, 2700000.0 ], [ 3200000.0, 2700000.0 ] ] ] } },
29
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE32N28", "EOFORIGIN": 3200000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3200000.0, 2800000.0 ], [ 3200000.0, 2900000.0 ], [ 3300000.0, 2900000.0 ], [ 3300000.0, 2800000.0 ], [ 3200000.0, 2800000.0 ] ] ] } },
30
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE32N29", "EOFORIGIN": 3200000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3200000.0, 2900000.0 ], [ 3200000.0, 3000000.0 ], [ 3300000.0, 3000000.0 ], [ 3300000.0, 2900000.0 ], [ 3200000.0, 2900000.0 ] ] ] } },
31
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE32N30", "EOFORIGIN": 3200000, "NOFORIGIN": 3000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3200000.0, 3000000.0 ], [ 3200000.0, 3100000.0 ], [ 3300000.0, 3100000.0 ], [ 3300000.0, 3000000.0 ], [ 3200000.0, 3000000.0 ] ] ] } },
32
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE33N22", "EOFORIGIN": 3300000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3300000.0, 2200000.0 ], [ 3300000.0, 2300000.0 ], [ 3400000.0, 2300000.0 ], [ 3400000.0, 2200000.0 ], [ 3300000.0, 2200000.0 ] ] ] } },
33
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE33N23", "EOFORIGIN": 3300000, "NOFORIGIN": 2300000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3300000.0, 2300000.0 ], [ 3300000.0, 2400000.0 ], [ 3400000.0, 2400000.0 ], [ 3400000.0, 2300000.0 ], [ 3300000.0, 2300000.0 ] ] ] } },
34
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE33N24", "EOFORIGIN": 3300000, "NOFORIGIN": 2400000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3300000.0, 2400000.0 ], [ 3300000.0, 2500000.0 ], [ 3400000.0, 2500000.0 ], [ 3400000.0, 2400000.0 ], [ 3300000.0, 2400000.0 ] ] ] } },
35
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE33N25", "EOFORIGIN": 3300000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3300000.0, 2500000.0 ], [ 3300000.0, 2600000.0 ], [ 3400000.0, 2600000.0 ], [ 3400000.0, 2500000.0 ], [ 3300000.0, 2500000.0 ] ] ] } },
36
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE33N26", "EOFORIGIN": 3300000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3300000.0, 2600000.0 ], [ 3300000.0, 2700000.0 ], [ 3400000.0, 2700000.0 ], [ 3400000.0, 2600000.0 ], [ 3300000.0, 2600000.0 ] ] ] } },
37
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE33N27", "EOFORIGIN": 3300000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3300000.0, 2700000.0 ], [ 3300000.0, 2800000.0 ], [ 3400000.0, 2800000.0 ], [ 3400000.0, 2700000.0 ], [ 3300000.0, 2700000.0 ] ] ] } },
38
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE33N28", "EOFORIGIN": 3300000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3300000.0, 2800000.0 ], [ 3300000.0, 2900000.0 ], [ 3400000.0, 2900000.0 ], [ 3400000.0, 2800000.0 ], [ 3300000.0, 2800000.0 ] ] ] } },
39
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE33N29", "EOFORIGIN": 3300000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3300000.0, 2900000.0 ], [ 3300000.0, 3000000.0 ], [ 3400000.0, 3000000.0 ], [ 3400000.0, 2900000.0 ], [ 3300000.0, 2900000.0 ] ] ] } },
40
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE33N30", "EOFORIGIN": 3300000, "NOFORIGIN": 3000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3300000.0, 3000000.0 ], [ 3300000.0, 3100000.0 ], [ 3400000.0, 3100000.0 ], [ 3400000.0, 3000000.0 ], [ 3300000.0, 3000000.0 ] ] ] } },
41
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE34N22", "EOFORIGIN": 3400000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3400000.0, 2200000.0 ], [ 3400000.0, 2300000.0 ], [ 3500000.0, 2300000.0 ], [ 3500000.0, 2200000.0 ], [ 3400000.0, 2200000.0 ] ] ] } },
42
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE34N23", "EOFORIGIN": 3400000, "NOFORIGIN": 2300000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3400000.0, 2300000.0 ], [ 3400000.0, 2400000.0 ], [ 3500000.0, 2400000.0 ], [ 3500000.0, 2300000.0 ], [ 3400000.0, 2300000.0 ] ] ] } },
43
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE34N24", "EOFORIGIN": 3400000, "NOFORIGIN": 2400000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3400000.0, 2400000.0 ], [ 3400000.0, 2500000.0 ], [ 3500000.0, 2500000.0 ], [ 3500000.0, 2400000.0 ], [ 3400000.0, 2400000.0 ] ] ] } },
44
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE34N25", "EOFORIGIN": 3400000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3400000.0, 2500000.0 ], [ 3400000.0, 2600000.0 ], [ 3500000.0, 2600000.0 ], [ 3500000.0, 2500000.0 ], [ 3400000.0, 2500000.0 ] ] ] } },
45
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE34N26", "EOFORIGIN": 3400000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3400000.0, 2600000.0 ], [ 3400000.0, 2700000.0 ], [ 3500000.0, 2700000.0 ], [ 3500000.0, 2600000.0 ], [ 3400000.0, 2600000.0 ] ] ] } },
46
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE34N27", "EOFORIGIN": 3400000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3400000.0, 2700000.0 ], [ 3400000.0, 2800000.0 ], [ 3500000.0, 2800000.0 ], [ 3500000.0, 2700000.0 ], [ 3400000.0, 2700000.0 ] ] ] } },
47
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE34N28", "EOFORIGIN": 3400000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3400000.0, 2800000.0 ], [ 3400000.0, 2900000.0 ], [ 3500000.0, 2900000.0 ], [ 3500000.0, 2800000.0 ], [ 3400000.0, 2800000.0 ] ] ] } },
48
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE34N29", "EOFORIGIN": 3400000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3400000.0, 2900000.0 ], [ 3400000.0, 3000000.0 ], [ 3500000.0, 3000000.0 ], [ 3500000.0, 2900000.0 ], [ 3400000.0, 2900000.0 ] ] ] } },
49
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE34N30", "EOFORIGIN": 3400000, "NOFORIGIN": 3000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3400000.0, 3000000.0 ], [ 3400000.0, 3100000.0 ], [ 3500000.0, 3100000.0 ], [ 3500000.0, 3000000.0 ], [ 3400000.0, 3000000.0 ] ] ] } },
50
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE35N22", "EOFORIGIN": 3500000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3500000.0, 2200000.0 ], [ 3500000.0, 2300000.0 ], [ 3600000.0, 2300000.0 ], [ 3600000.0, 2200000.0 ], [ 3500000.0, 2200000.0 ] ] ] } },
51
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE35N23", "EOFORIGIN": 3500000, "NOFORIGIN": 2300000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3500000.0, 2300000.0 ], [ 3500000.0, 2400000.0 ], [ 3600000.0, 2400000.0 ], [ 3600000.0, 2300000.0 ], [ 3500000.0, 2300000.0 ] ] ] } },
52
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE35N24", "EOFORIGIN": 3500000, "NOFORIGIN": 2400000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3500000.0, 2400000.0 ], [ 3500000.0, 2500000.0 ], [ 3600000.0, 2500000.0 ], [ 3600000.0, 2400000.0 ], [ 3500000.0, 2400000.0 ] ] ] } },
53
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE35N25", "EOFORIGIN": 3500000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3500000.0, 2500000.0 ], [ 3500000.0, 2600000.0 ], [ 3600000.0, 2600000.0 ], [ 3600000.0, 2500000.0 ], [ 3500000.0, 2500000.0 ] ] ] } },
54
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE35N26", "EOFORIGIN": 3500000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3500000.0, 2600000.0 ], [ 3500000.0, 2700000.0 ], [ 3600000.0, 2700000.0 ], [ 3600000.0, 2600000.0 ], [ 3500000.0, 2600000.0 ] ] ] } },
55
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE35N27", "EOFORIGIN": 3500000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3500000.0, 2700000.0 ], [ 3500000.0, 2800000.0 ], [ 3600000.0, 2800000.0 ], [ 3600000.0, 2700000.0 ], [ 3500000.0, 2700000.0 ] ] ] } },
56
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE35N28", "EOFORIGIN": 3500000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3500000.0, 2800000.0 ], [ 3500000.0, 2900000.0 ], [ 3600000.0, 2900000.0 ], [ 3600000.0, 2800000.0 ], [ 3500000.0, 2800000.0 ] ] ] } },
57
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE35N29", "EOFORIGIN": 3500000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3500000.0, 2900000.0 ], [ 3500000.0, 3000000.0 ], [ 3600000.0, 3000000.0 ], [ 3600000.0, 2900000.0 ], [ 3500000.0, 2900000.0 ] ] ] } },
58
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE35N30", "EOFORIGIN": 3500000, "NOFORIGIN": 3000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3500000.0, 3000000.0 ], [ 3500000.0, 3100000.0 ], [ 3600000.0, 3100000.0 ], [ 3600000.0, 3000000.0 ], [ 3500000.0, 3000000.0 ] ] ] } },
59
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N21", "EOFORIGIN": 3600000, "NOFORIGIN": 2100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 2100000.0 ], [ 3600000.0, 2200000.0 ], [ 3700000.0, 2200000.0 ], [ 3700000.0, 2100000.0 ], [ 3600000.0, 2100000.0 ] ] ] } },
60
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N22", "EOFORIGIN": 3600000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 2200000.0 ], [ 3600000.0, 2300000.0 ], [ 3700000.0, 2300000.0 ], [ 3700000.0, 2200000.0 ], [ 3600000.0, 2200000.0 ] ] ] } },
61
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N23", "EOFORIGIN": 3600000, "NOFORIGIN": 2300000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 2300000.0 ], [ 3600000.0, 2400000.0 ], [ 3700000.0, 2400000.0 ], [ 3700000.0, 2300000.0 ], [ 3600000.0, 2300000.0 ] ] ] } },
62
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N24", "EOFORIGIN": 3600000, "NOFORIGIN": 2400000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 2400000.0 ], [ 3600000.0, 2500000.0 ], [ 3700000.0, 2500000.0 ], [ 3700000.0, 2400000.0 ], [ 3600000.0, 2400000.0 ] ] ] } },
63
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N25", "EOFORIGIN": 3600000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 2500000.0 ], [ 3600000.0, 2600000.0 ], [ 3700000.0, 2600000.0 ], [ 3700000.0, 2500000.0 ], [ 3600000.0, 2500000.0 ] ] ] } },
64
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N26", "EOFORIGIN": 3600000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 2600000.0 ], [ 3600000.0, 2700000.0 ], [ 3700000.0, 2700000.0 ], [ 3700000.0, 2600000.0 ], [ 3600000.0, 2600000.0 ] ] ] } },
65
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N27", "EOFORIGIN": 3600000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 2700000.0 ], [ 3600000.0, 2800000.0 ], [ 3700000.0, 2800000.0 ], [ 3700000.0, 2700000.0 ], [ 3600000.0, 2700000.0 ] ] ] } },
66
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N28", "EOFORIGIN": 3600000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 2800000.0 ], [ 3600000.0, 2900000.0 ], [ 3700000.0, 2900000.0 ], [ 3700000.0, 2800000.0 ], [ 3600000.0, 2800000.0 ] ] ] } },
67
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N29", "EOFORIGIN": 3600000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 2900000.0 ], [ 3600000.0, 3000000.0 ], [ 3700000.0, 3000000.0 ], [ 3700000.0, 2900000.0 ], [ 3600000.0, 2900000.0 ] ] ] } },
68
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N30", "EOFORIGIN": 3600000, "NOFORIGIN": 3000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 3000000.0 ], [ 3600000.0, 3100000.0 ], [ 3700000.0, 3100000.0 ], [ 3700000.0, 3000000.0 ], [ 3600000.0, 3000000.0 ] ] ] } },
69
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE36N31", "EOFORIGIN": 3600000, "NOFORIGIN": 3100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3600000.0, 3100000.0 ], [ 3600000.0, 3200000.0 ], [ 3700000.0, 3200000.0 ], [ 3700000.0, 3100000.0 ], [ 3600000.0, 3100000.0 ] ] ] } },
70
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N21", "EOFORIGIN": 3700000, "NOFORIGIN": 2100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 2100000.0 ], [ 3700000.0, 2200000.0 ], [ 3800000.0, 2200000.0 ], [ 3800000.0, 2100000.0 ], [ 3700000.0, 2100000.0 ] ] ] } },
71
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N22", "EOFORIGIN": 3700000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 2200000.0 ], [ 3700000.0, 2300000.0 ], [ 3800000.0, 2300000.0 ], [ 3800000.0, 2200000.0 ], [ 3700000.0, 2200000.0 ] ] ] } },
72
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N23", "EOFORIGIN": 3700000, "NOFORIGIN": 2300000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 2300000.0 ], [ 3700000.0, 2400000.0 ], [ 3800000.0, 2400000.0 ], [ 3800000.0, 2300000.0 ], [ 3700000.0, 2300000.0 ] ] ] } },
73
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N24", "EOFORIGIN": 3700000, "NOFORIGIN": 2400000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 2400000.0 ], [ 3700000.0, 2500000.0 ], [ 3800000.0, 2500000.0 ], [ 3800000.0, 2400000.0 ], [ 3700000.0, 2400000.0 ] ] ] } },
74
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N25", "EOFORIGIN": 3700000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 2500000.0 ], [ 3700000.0, 2600000.0 ], [ 3800000.0, 2600000.0 ], [ 3800000.0, 2500000.0 ], [ 3700000.0, 2500000.0 ] ] ] } },
75
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N26", "EOFORIGIN": 3700000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 2600000.0 ], [ 3700000.0, 2700000.0 ], [ 3800000.0, 2700000.0 ], [ 3800000.0, 2600000.0 ], [ 3700000.0, 2600000.0 ] ] ] } },
76
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N27", "EOFORIGIN": 3700000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 2700000.0 ], [ 3700000.0, 2800000.0 ], [ 3800000.0, 2800000.0 ], [ 3800000.0, 2700000.0 ], [ 3700000.0, 2700000.0 ] ] ] } },
77
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N28", "EOFORIGIN": 3700000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 2800000.0 ], [ 3700000.0, 2900000.0 ], [ 3800000.0, 2900000.0 ], [ 3800000.0, 2800000.0 ], [ 3700000.0, 2800000.0 ] ] ] } },
78
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N29", "EOFORIGIN": 3700000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 2900000.0 ], [ 3700000.0, 3000000.0 ], [ 3800000.0, 3000000.0 ], [ 3800000.0, 2900000.0 ], [ 3700000.0, 2900000.0 ] ] ] } },
79
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N30", "EOFORIGIN": 3700000, "NOFORIGIN": 3000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 3000000.0 ], [ 3700000.0, 3100000.0 ], [ 3800000.0, 3100000.0 ], [ 3800000.0, 3000000.0 ], [ 3700000.0, 3000000.0 ] ] ] } },
80
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N31", "EOFORIGIN": 3700000, "NOFORIGIN": 3100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 3100000.0 ], [ 3700000.0, 3200000.0 ], [ 3800000.0, 3200000.0 ], [ 3800000.0, 3100000.0 ], [ 3700000.0, 3100000.0 ] ] ] } },
81
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE37N32", "EOFORIGIN": 3700000, "NOFORIGIN": 3200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3700000.0, 3200000.0 ], [ 3700000.0, 3300000.0 ], [ 3800000.0, 3300000.0 ], [ 3800000.0, 3200000.0 ], [ 3700000.0, 3200000.0 ] ] ] } },
82
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N20", "EOFORIGIN": 3800000, "NOFORIGIN": 2000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 2000000.0 ], [ 3800000.0, 2100000.0 ], [ 3900000.0, 2100000.0 ], [ 3900000.0, 2000000.0 ], [ 3800000.0, 2000000.0 ] ] ] } },
83
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N21", "EOFORIGIN": 3800000, "NOFORIGIN": 2100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 2100000.0 ], [ 3800000.0, 2200000.0 ], [ 3900000.0, 2200000.0 ], [ 3900000.0, 2100000.0 ], [ 3800000.0, 2100000.0 ] ] ] } },
84
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N22", "EOFORIGIN": 3800000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 2200000.0 ], [ 3800000.0, 2300000.0 ], [ 3900000.0, 2300000.0 ], [ 3900000.0, 2200000.0 ], [ 3800000.0, 2200000.0 ] ] ] } },
85
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N23", "EOFORIGIN": 3800000, "NOFORIGIN": 2300000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 2300000.0 ], [ 3800000.0, 2400000.0 ], [ 3900000.0, 2400000.0 ], [ 3900000.0, 2300000.0 ], [ 3800000.0, 2300000.0 ] ] ] } },
86
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N24", "EOFORIGIN": 3800000, "NOFORIGIN": 2400000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 2400000.0 ], [ 3800000.0, 2500000.0 ], [ 3900000.0, 2500000.0 ], [ 3900000.0, 2400000.0 ], [ 3800000.0, 2400000.0 ] ] ] } },
87
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N25", "EOFORIGIN": 3800000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 2500000.0 ], [ 3800000.0, 2600000.0 ], [ 3900000.0, 2600000.0 ], [ 3900000.0, 2500000.0 ], [ 3800000.0, 2500000.0 ] ] ] } },
88
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N26", "EOFORIGIN": 3800000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 2600000.0 ], [ 3800000.0, 2700000.0 ], [ 3900000.0, 2700000.0 ], [ 3900000.0, 2600000.0 ], [ 3800000.0, 2600000.0 ] ] ] } },
89
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N27", "EOFORIGIN": 3800000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 2700000.0 ], [ 3800000.0, 2800000.0 ], [ 3900000.0, 2800000.0 ], [ 3900000.0, 2700000.0 ], [ 3800000.0, 2700000.0 ] ] ] } },
90
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N28", "EOFORIGIN": 3800000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 2800000.0 ], [ 3800000.0, 2900000.0 ], [ 3900000.0, 2900000.0 ], [ 3900000.0, 2800000.0 ], [ 3800000.0, 2800000.0 ] ] ] } },
91
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N29", "EOFORIGIN": 3800000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 2900000.0 ], [ 3800000.0, 3000000.0 ], [ 3900000.0, 3000000.0 ], [ 3900000.0, 2900000.0 ], [ 3800000.0, 2900000.0 ] ] ] } },
92
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N30", "EOFORIGIN": 3800000, "NOFORIGIN": 3000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 3000000.0 ], [ 3800000.0, 3100000.0 ], [ 3900000.0, 3100000.0 ], [ 3900000.0, 3000000.0 ], [ 3800000.0, 3000000.0 ] ] ] } },
93
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE38N31", "EOFORIGIN": 3800000, "NOFORIGIN": 3100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3800000.0, 3100000.0 ], [ 3800000.0, 3200000.0 ], [ 3900000.0, 3200000.0 ], [ 3900000.0, 3100000.0 ], [ 3800000.0, 3100000.0 ] ] ] } },
94
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N19", "EOFORIGIN": 3900000, "NOFORIGIN": 1900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 1900000.0 ], [ 3900000.0, 2000000.0 ], [ 4000000.0, 2000000.0 ], [ 4000000.0, 1900000.0 ], [ 3900000.0, 1900000.0 ] ] ] } },
95
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N20", "EOFORIGIN": 3900000, "NOFORIGIN": 2000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 2000000.0 ], [ 3900000.0, 2100000.0 ], [ 4000000.0, 2100000.0 ], [ 4000000.0, 2000000.0 ], [ 3900000.0, 2000000.0 ] ] ] } },
96
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N21", "EOFORIGIN": 3900000, "NOFORIGIN": 2100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 2100000.0 ], [ 3900000.0, 2200000.0 ], [ 4000000.0, 2200000.0 ], [ 4000000.0, 2100000.0 ], [ 3900000.0, 2100000.0 ] ] ] } },
97
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N22", "EOFORIGIN": 3900000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 2200000.0 ], [ 3900000.0, 2300000.0 ], [ 4000000.0, 2300000.0 ], [ 4000000.0, 2200000.0 ], [ 3900000.0, 2200000.0 ] ] ] } },
98
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N23", "EOFORIGIN": 3900000, "NOFORIGIN": 2300000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 2300000.0 ], [ 3900000.0, 2400000.0 ], [ 4000000.0, 2400000.0 ], [ 4000000.0, 2300000.0 ], [ 3900000.0, 2300000.0 ] ] ] } },
99
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N24", "EOFORIGIN": 3900000, "NOFORIGIN": 2400000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 2400000.0 ], [ 3900000.0, 2500000.0 ], [ 4000000.0, 2500000.0 ], [ 4000000.0, 2400000.0 ], [ 3900000.0, 2400000.0 ] ] ] } },
100
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N25", "EOFORIGIN": 3900000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 2500000.0 ], [ 3900000.0, 2600000.0 ], [ 4000000.0, 2600000.0 ], [ 4000000.0, 2500000.0 ], [ 3900000.0, 2500000.0 ] ] ] } },
101
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N26", "EOFORIGIN": 3900000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 2600000.0 ], [ 3900000.0, 2700000.0 ], [ 4000000.0, 2700000.0 ], [ 4000000.0, 2600000.0 ], [ 3900000.0, 2600000.0 ] ] ] } },
102
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N27", "EOFORIGIN": 3900000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 2700000.0 ], [ 3900000.0, 2800000.0 ], [ 4000000.0, 2800000.0 ], [ 4000000.0, 2700000.0 ], [ 3900000.0, 2700000.0 ] ] ] } },
103
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N28", "EOFORIGIN": 3900000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 2800000.0 ], [ 3900000.0, 2900000.0 ], [ 4000000.0, 2900000.0 ], [ 4000000.0, 2800000.0 ], [ 3900000.0, 2800000.0 ] ] ] } },
104
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N29", "EOFORIGIN": 3900000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 2900000.0 ], [ 3900000.0, 3000000.0 ], [ 4000000.0, 3000000.0 ], [ 4000000.0, 2900000.0 ], [ 3900000.0, 2900000.0 ] ] ] } },
105
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE39N30", "EOFORIGIN": 3900000, "NOFORIGIN": 3000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3900000.0, 3000000.0 ], [ 3900000.0, 3100000.0 ], [ 4000000.0, 3100000.0 ], [ 4000000.0, 3000000.0 ], [ 3900000.0, 3000000.0 ] ] ] } },
106
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE40N20", "EOFORIGIN": 4000000, "NOFORIGIN": 2000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4000000.0, 2000000.0 ], [ 4000000.0, 2100000.0 ], [ 4100000.0, 2100000.0 ], [ 4100000.0, 2000000.0 ], [ 4000000.0, 2000000.0 ] ] ] } },
107
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE40N21", "EOFORIGIN": 4000000, "NOFORIGIN": 2100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4000000.0, 2100000.0 ], [ 4000000.0, 2200000.0 ], [ 4100000.0, 2200000.0 ], [ 4100000.0, 2100000.0 ], [ 4000000.0, 2100000.0 ] ] ] } },
108
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE40N22", "EOFORIGIN": 4000000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4000000.0, 2200000.0 ], [ 4000000.0, 2300000.0 ], [ 4100000.0, 2300000.0 ], [ 4100000.0, 2200000.0 ], [ 4000000.0, 2200000.0 ] ] ] } },
109
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE40N23", "EOFORIGIN": 4000000, "NOFORIGIN": 2300000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4000000.0, 2300000.0 ], [ 4000000.0, 2400000.0 ], [ 4100000.0, 2400000.0 ], [ 4100000.0, 2300000.0 ], [ 4000000.0, 2300000.0 ] ] ] } },
110
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE40N24", "EOFORIGIN": 4000000, "NOFORIGIN": 2400000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4000000.0, 2400000.0 ], [ 4000000.0, 2500000.0 ], [ 4100000.0, 2500000.0 ], [ 4100000.0, 2400000.0 ], [ 4000000.0, 2400000.0 ] ] ] } },
111
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE40N25", "EOFORIGIN": 4000000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4000000.0, 2500000.0 ], [ 4000000.0, 2600000.0 ], [ 4100000.0, 2600000.0 ], [ 4100000.0, 2500000.0 ], [ 4000000.0, 2500000.0 ] ] ] } },
112
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE40N26", "EOFORIGIN": 4000000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4000000.0, 2600000.0 ], [ 4000000.0, 2700000.0 ], [ 4100000.0, 2700000.0 ], [ 4100000.0, 2600000.0 ], [ 4000000.0, 2600000.0 ] ] ] } },
113
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE40N27", "EOFORIGIN": 4000000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4000000.0, 2700000.0 ], [ 4000000.0, 2800000.0 ], [ 4100000.0, 2800000.0 ], [ 4100000.0, 2700000.0 ], [ 4000000.0, 2700000.0 ] ] ] } },
114
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE40N28", "EOFORIGIN": 4000000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4000000.0, 2800000.0 ], [ 4000000.0, 2900000.0 ], [ 4100000.0, 2900000.0 ], [ 4100000.0, 2800000.0 ], [ 4000000.0, 2800000.0 ] ] ] } },
115
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE40N29", "EOFORIGIN": 4000000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4000000.0, 2900000.0 ], [ 4000000.0, 3000000.0 ], [ 4100000.0, 3000000.0 ], [ 4100000.0, 2900000.0 ], [ 4000000.0, 2900000.0 ] ] ] } },
116
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE41N20", "EOFORIGIN": 4100000, "NOFORIGIN": 2000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4100000.0, 2000000.0 ], [ 4100000.0, 2100000.0 ], [ 4200000.0, 2100000.0 ], [ 4200000.0, 2000000.0 ], [ 4100000.0, 2000000.0 ] ] ] } },
117
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE41N21", "EOFORIGIN": 4100000, "NOFORIGIN": 2100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4100000.0, 2100000.0 ], [ 4100000.0, 2200000.0 ], [ 4200000.0, 2200000.0 ], [ 4200000.0, 2100000.0 ], [ 4100000.0, 2100000.0 ] ] ] } },
118
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE41N22", "EOFORIGIN": 4100000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4100000.0, 2200000.0 ], [ 4100000.0, 2300000.0 ], [ 4200000.0, 2300000.0 ], [ 4200000.0, 2200000.0 ], [ 4100000.0, 2200000.0 ] ] ] } },
119
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE41N23", "EOFORIGIN": 4100000, "NOFORIGIN": 2300000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4100000.0, 2300000.0 ], [ 4100000.0, 2400000.0 ], [ 4200000.0, 2400000.0 ], [ 4200000.0, 2300000.0 ], [ 4100000.0, 2300000.0 ] ] ] } },
120
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE41N24", "EOFORIGIN": 4100000, "NOFORIGIN": 2400000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4100000.0, 2400000.0 ], [ 4100000.0, 2500000.0 ], [ 4200000.0, 2500000.0 ], [ 4200000.0, 2400000.0 ], [ 4100000.0, 2400000.0 ] ] ] } },
121
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE41N25", "EOFORIGIN": 4100000, "NOFORIGIN": 2500000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4100000.0, 2500000.0 ], [ 4100000.0, 2600000.0 ], [ 4200000.0, 2600000.0 ], [ 4200000.0, 2500000.0 ], [ 4100000.0, 2500000.0 ] ] ] } },
122
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE41N26", "EOFORIGIN": 4100000, "NOFORIGIN": 2600000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4100000.0, 2600000.0 ], [ 4100000.0, 2700000.0 ], [ 4200000.0, 2700000.0 ], [ 4200000.0, 2600000.0 ], [ 4100000.0, 2600000.0 ] ] ] } },
123
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE41N27", "EOFORIGIN": 4100000, "NOFORIGIN": 2700000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4100000.0, 2700000.0 ], [ 4100000.0, 2800000.0 ], [ 4200000.0, 2800000.0 ], [ 4200000.0, 2700000.0 ], [ 4100000.0, 2700000.0 ] ] ] } },
124
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE41N28", "EOFORIGIN": 4100000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4100000.0, 2800000.0 ], [ 4100000.0, 2900000.0 ], [ 4200000.0, 2900000.0 ], [ 4200000.0, 2800000.0 ], [ 4100000.0, 2800000.0 ] ] ] } },
125
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE41N29", "EOFORIGIN": 4100000, "NOFORIGIN": 2900000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4100000.0, 2900000.0 ], [ 4100000.0, 3000000.0 ], [ 4200000.0, 3000000.0 ], [ 4200000.0, 2900000.0 ], [ 4100000.0, 2900000.0 ] ] ] } },
126
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE42N20", "EOFORIGIN": 4200000, "NOFORIGIN": 2000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4200000.0, 2000000.0 ], [ 4200000.0, 2100000.0 ], [ 4300000.0, 2100000.0 ], [ 4300000.0, 2000000.0 ], [ 4200000.0, 2000000.0 ] ] ] } },
127
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE42N21", "EOFORIGIN": 4200000, "NOFORIGIN": 2100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4200000.0, 2100000.0 ], [ 4200000.0, 2200000.0 ], [ 4300000.0, 2200000.0 ], [ 4300000.0, 2100000.0 ], [ 4200000.0, 2100000.0 ] ] ] } },
128
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE42N22", "EOFORIGIN": 4200000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4200000.0, 2200000.0 ], [ 4200000.0, 2300000.0 ], [ 4300000.0, 2300000.0 ], [ 4300000.0, 2200000.0 ], [ 4200000.0, 2200000.0 ] ] ] } },
129
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE42N28", "EOFORIGIN": 4200000, "NOFORIGIN": 2800000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4200000.0, 2800000.0 ], [ 4200000.0, 2900000.0 ], [ 4300000.0, 2900000.0 ], [ 4300000.0, 2800000.0 ], [ 4200000.0, 2800000.0 ] ] ] } },
130
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE43N20", "EOFORIGIN": 4300000, "NOFORIGIN": 2000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4300000.0, 2000000.0 ], [ 4300000.0, 2100000.0 ], [ 4400000.0, 2100000.0 ], [ 4400000.0, 2000000.0 ], [ 4300000.0, 2000000.0 ] ] ] } },
131
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE43N21", "EOFORIGIN": 4300000, "NOFORIGIN": 2100000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4300000.0, 2100000.0 ], [ 4300000.0, 2200000.0 ], [ 4400000.0, 2200000.0 ], [ 4400000.0, 2100000.0 ], [ 4300000.0, 2100000.0 ] ] ] } },
132
+ { "type": "Feature", "properties": { "CELLCODE": "100kmE43N22", "EOFORIGIN": 4300000, "NOFORIGIN": 2200000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 4300000.0, 2200000.0 ], [ 4300000.0, 2300000.0 ], [ 4400000.0, 2300000.0 ], [ 4400000.0, 2200000.0 ], [ 4300000.0, 2200000.0 ] ] ] } }
133
+ ]
134
+ }
data/departements.geojson ADDED
The diff for this file is too large to render. See raw diff
 
data/regions.geojson ADDED
The diff for this file is too large to render. See raw diff
 
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ python-dotenv
2
+ mistralai
3
+ langchain-community
4
+ faiss-cpu
5
+ gradio
6
+ plotly
static/layout.html ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <link rel="stylesheet" href="/static/style.css" />
4
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
5
+
6
+ <header>
7
+ <nav>
8
+ <ul class="menus">
9
+ <li class="main" id="main_menu">
10
+ <a href="#chat">
11
+ <img src="https://next.ink/wp-content/uploads/2024/02/announcing-mistral.png" alt="Mistral" class="mistral" />
12
+ </a>
13
+ </li>
14
+ <li class="menu" id="profile">
15
+ <a href="#profile"><i class="fas fa-home"></i></a>
16
+ </li>
17
+
18
+ <li class="menu" id="weather">
19
+ <a href="#weather"><i class="fas fa-cloud-sun"></i></a>
20
+ </li>
21
+
22
+ <li class="menu" id="news">
23
+ <a href="#news"><i class="fas fa-newspaper"></i></a>
24
+ </li>
25
+
26
+ <!-- videos -->
27
+ <li class="menu" id="videos">
28
+ <a href="#videos"><i class="fas fa-video"></i></a>
29
+ </li>
30
+ </ul>
31
+ </nav>
32
+ </header>
33
+
34
+ <div class="app flexed">
35
+ <div class="spacer" id="menu_spacer">
36
+ <ul class="submenus">
37
+ <li class="submenu">
38
+ <div id="chat">
39
+ <p>Posez vos questions (administratives, sociales) à votre assistant Mistral !</p>
40
+ <ul id="chat_messages"></ul>
41
+ <input
42
+ type="text"
43
+ id="user_input"
44
+ placeholder="Discutez avec Mistral"
45
+ />
46
+ <button class="button1" onclick="sendChat()">Envoyer</button>
47
+ </div>
48
+ </li>
49
+ </ul>
50
+ </div>
51
+ </div>
52
+
53
+ <div class="app flexed">
54
+ <div class="spacer" id="profile_spacer">
55
+ <ul class="submenus">
56
+ <li class="submenu">
57
+ <a href="#profile">Profile</a>
58
+ <div id="profile">
59
+ <ul>
60
+ <li>Nom: {{ user_profile.name }}</li>
61
+ <li>Age: {{ user_profile.age }}</li>
62
+ <li>Localisation: {{ user_profile.location }}</li>
63
+ <button class="button1" onclick="sendReport()">Générer une newsletter Mistral</button>
64
+ </ul>
65
+ <p> Cette fonctionnalité apprend des actualités en temps réel et digère une très grande quantité d'informations sur le monde agricole. </p>
66
+ </div>
67
+ </li>
68
+ </ul>
69
+ </div>
70
+ </div>
71
+
72
+ <div class="app flexed">
73
+ <div class="spacer" id="news_spacer">
74
+ <ul class="submenus">
75
+ <li class="submenu">
76
+ <div id="news">
77
+ <h2>Les dernières nouvelles</h2>
78
+ <ul>
79
+ <li>Paysan-breton:</li>
80
+ <link>
81
+ https://www.paysan-breton.fr/2024/02/saint-brieuc-fait-son-retour-a-la-terre/
82
+ </link>
83
+
84
+ <li>La France Agricole:</li>
85
+ <link>https://www.lafranceagricole.fr/crise-agricole/article/863578/bruno-le-maire-demande-un-effort-aux-banques-et-aux-assurances</link>
86
+ <li>Terre-net:</li>
87
+ <link>https://www.terre-net.fr/reglementation/article/863560/l-hypothese-d-un-permis-tracteur-europeen-met-le-feu-aux-poudres</link>
88
+ </ul>
89
+ </div>
90
+ </li>
91
+ </ul>
92
+ </div>
93
+ </div>
94
+
95
+ <div class="app flexed">
96
+ <div class="spacer" id="weather_spacer">
97
+ <ul class="submenus">
98
+ <li class="submenu">
99
+ <div id="weather">
100
+ <ul>
101
+ <li>Bulletin météo</li>
102
+ <iframe width="300" height="450" src="https://embed.windy.com/embed.html?type=map&location=coordinates&metricRain=mm&metricTemp=°C&metricWind=default&zoom=7&overlay=wind&product=ecmwf&level=surface&lat=46.965&lon=0.813&detailLat=47.636&detailLon=0.813&detail=true&pressure=true" frameborder="0"></iframe>
103
+ <li>Webcams en temps réel</li>
104
+ <a name="windy-webcam-timelapse-player" data-id="1619075844" data-play="month" href="https://windy.com/webcams/1619075844" target="_blank">La Lande-Patry: Flers - Saint-Paul Airport</a><script async type="text/javascript" src="https://webcams.windy.com/webcams/public/embed/script/player.js"></script>
105
+ </ul>
106
+ </div>
107
+ </li>
108
+ </ul>
109
+ </div>
110
+
111
+ <div class="app flexed">
112
+ <div class="spacer" id="videos_spacer">
113
+ <ul class="submenus">
114
+ <li class="submenu">
115
+ <div id="videos">
116
+ <ul>
117
+ <li>Les vidéos du jour</li>
118
+ <iframe width="200" height="300" src="https://www.youtube.com/embed/yd9agyhuDp4?si=tpfszKgmmcx9zoGl&amp;controls=0&amp;start=1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
119
+ <iframe width="200" height="300" src="https://www.youtube.com/embed/oPcG6hpKNDc?si=-SaICP4xXHS1p_WN" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
120
+ </ul>
121
+ </div>
122
+ </li>
123
+ </ul>
124
+ </div>
125
+ </div>
126
+
127
+ <body>
128
+ <div id="title">
129
+ <p class="sub_title">Accédez aux différents onglets sur votre gauche</p>
130
+ </div>
131
+ <div id="mainContent">
132
+ <div id="map" class="map">{{ map_html | safe }}</div>
133
+ </div>
134
+ <div id="footer">
135
+ <p>© 2024 AgriHackteurs</p>
136
+ </body>
137
+ <script src="/static/script.js"></script>
138
+ </html>
static/script.js ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ if (document.querySelector(".main")) {
3
+ document.querySelector(".main").onclick = function () {
4
+ var submenu = document.querySelector("#menu_spacer");
5
+ submenu.classList.toggle("show");
6
+ }
7
+ }
8
+
9
+ if (document.querySelector("#profile")) {
10
+ document.querySelector("#profile").onclick = function () {
11
+ var submenu = document.querySelector("#profile_spacer");
12
+ submenu.classList.toggle("show");
13
+ }
14
+ }
15
+
16
+ if (document.querySelector("#weather")) {
17
+ document.querySelector("#weather").onclick = function () {
18
+ var submenu = document.querySelector("#weather_spacer");
19
+ submenu.classList.toggle("show");
20
+ }
21
+ }
22
+
23
+ if (document.querySelector("#news")) {
24
+ document.querySelector("#news").onclick = function () {
25
+ var submenu = document.querySelector("#news_spacer");
26
+ submenu.classList.toggle("show");
27
+ }
28
+ }
29
+
30
+ // videos
31
+ if (document.querySelector("#videos")) {
32
+ document.querySelector("#videos").onclick = function () {
33
+ var submenu = document.querySelector("#videos_spacer");
34
+ submenu.classList.toggle("show");
35
+ }
36
+ }
37
+
38
+ function sendChat() {
39
+ var user_input = document.getElementById("user_input").value;
40
+ var chat_messages = document.getElementById("chat_messages");
41
+
42
+ var user_message = document.createElement("li");
43
+ user_message.className = "chat-message"; // add a class to style the li elements
44
+
45
+ var user_image = document.createElement("img");
46
+ user_image.src = "https://cdn.icon-icons.com/icons2/1465/PNG/512/138manfarmer2_100718.png"; // replace with the actual path to the image
47
+ user_image.id = "user-image"; // add an id to style the image
48
+ user_message.appendChild(user_image);
49
+
50
+ var user_text = document.createElement("span"); // wrap the text in a span element
51
+ user_text.appendChild(document.createTextNode(user_input));
52
+ user_message.appendChild(user_text);
53
+
54
+ chat_messages.appendChild(user_message);
55
+ document.getElementById("user_input").value = "";
56
+
57
+ fetch('/chat', {
58
+ method: 'POST',
59
+ headers: {
60
+ 'Content-Type': 'application/json',
61
+ },
62
+ body: JSON.stringify({
63
+ user_input: user_input
64
+ }),
65
+ })
66
+ .then(response => response.text())
67
+ .then(data => {
68
+ var mistral_message = document.createElement("li");
69
+ mistral_message.className = "chat-message"; // add a class to style the li elements
70
+
71
+ var mistral_image = document.createElement("img");
72
+ mistral_image.src = "https://next.ink/wp-content/uploads/2024/02/announcing-mistral.png";
73
+ mistral_image.id = "mistral-image"; // add an id to style the image
74
+ mistral_message.appendChild(mistral_image);
75
+
76
+ var mistral_text = document.createElement("b"); // wrap the text in a b element
77
+ mistral_text.appendChild(document.createTextNode(data));
78
+ mistral_message.appendChild(mistral_text);
79
+
80
+ chat_messages.appendChild(mistral_message);
81
+ });
82
+ }
83
+
84
+ function sendReport() {
85
+ fetch('/report', {
86
+ method: 'GET',
87
+ headers: {
88
+ 'Content-Type': 'application/json',
89
+ }
90
+ })
91
+ .then(response => response.text())
92
+ .then(data => {
93
+ var report = document.createElement("li");
94
+ report.className = "submenu"; // add a class to style the li elements
95
+
96
+ var report_text = document.createElement("b"); // wrap the text in a b element
97
+ report_text.appendChild(document.createTextNode(data));
98
+ report.appendChild(report_text);
99
+
100
+ document.querySelector("#profile ul").appendChild(report);
101
+ });
102
+ }
103
+
static/style.css ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ html {
2
+ font-family: sans-serif;
3
+ -webkit-text-size-adjust: 100%;
4
+ -ms-text-size-adjust: 100%;
5
+ }
6
+
7
+ h1,
8
+ h2,
9
+ li,
10
+ p {
11
+ color: white;
12
+ }
13
+
14
+ .main_title {
15
+ color: #f1f1f1;
16
+ text-align: center;
17
+ }
18
+
19
+ body {
20
+ background-image: url('https://lh3.googleusercontent.com/JKE8WODi6oggtccvEyMnYswDLqSVjDv4FqIGec2qF1doGXf3HTJ5MnMHqG-thNklmxKO6aGf23XiZAFwbaSxt4sTyWc-IT-zyH6aQA=rj-w0-h1600-l80');
21
+ background-repeat: no-repeat;
22
+ background-size: cover;
23
+ margin: 0;
24
+ color: #f1f1f1;
25
+
26
+ }
27
+
28
+ header,
29
+ nav {
30
+ display: block;
31
+ }
32
+
33
+ a {
34
+ background-color: transparent;
35
+ color: inherit;
36
+ text-decoration: none;
37
+ }
38
+
39
+ a:active,
40
+ a:hover {
41
+ outline: 0;
42
+ }
43
+
44
+ img {
45
+ border: 0;
46
+ }
47
+
48
+ ul {
49
+ list-style: none;
50
+ margin: 0;
51
+ -webkit-margin-before: 0;
52
+ -webkit-margin-after: 0;
53
+ -webkit-margin-start: 0;
54
+ -webkit-margin-end: 0;
55
+ -webkit-padding-start: 0px;
56
+ }
57
+
58
+ header {
59
+ position: fixed;
60
+ top: 0;
61
+ left: 0;
62
+ bottom: 0;
63
+ width: 100px;
64
+ height: 100%;
65
+ min-height: 0;
66
+ min-width: 100px;
67
+ overflow-y: auto;
68
+ transition: 500ms;
69
+ background-color: rgb(56, 82, 102);
70
+ border-right: 1px solid #ffffff80;
71
+ z-index: 10;
72
+ }
73
+
74
+ .flexed {
75
+ display: flex;
76
+ }
77
+
78
+ .spacer {
79
+ display: inline-block;
80
+ position: relative;
81
+ height: 100%;
82
+ width: 10vw;
83
+ min-width: 110px;
84
+ }
85
+
86
+ .menus {
87
+ display: flex;
88
+ flex-direction: column;
89
+ justify-content: flex-start;
90
+ align-items: center;
91
+ width: 100%;
92
+ min-height: 400px;
93
+ text-align: center;
94
+ }
95
+
96
+ .main {
97
+ padding: 1em 1em 0.8em;
98
+ border-bottom: 1px solid #FFF;
99
+ color: #f1f1f1;
100
+ }
101
+
102
+ .menu {
103
+ margin: 0.5em 0 0;
104
+ color: #f1f1f1;
105
+ transition: 0.5s;
106
+ font-size: 1.5rem;
107
+ padding: 0.5em 0;
108
+ border-bottom: 1px solid #ececec;
109
+ width: 33.333%;
110
+ }
111
+
112
+ .menu:hover {
113
+ color: #ececec;
114
+ background-color: rgba(11, 123, 132, 1);
115
+ border: 1px solid #FFF;
116
+ border-radius: 50%;
117
+ padding: 0.3em;
118
+ box-shadow: 0 0 5px 0 #000;
119
+ }
120
+
121
+ .spacer {
122
+ display: flex;
123
+ flex: 1;
124
+ position: relative;
125
+ height: 100%;
126
+ min-width: 110px;
127
+ transition: 300ms;
128
+ }
129
+
130
+ .spacer.show {
131
+ width: auto;
132
+ margin-right: 1em;
133
+ }
134
+
135
+ .spacer .submenus {
136
+ display: none;
137
+ }
138
+
139
+ .spacer.show .submenus {
140
+ display: flex;
141
+ flex-direction: column;
142
+ justify-content: flex-start;
143
+ width: 300px;
144
+ height: 100vh;
145
+ min-height: 100px;
146
+ margin-left: 100px;
147
+ padding-left: 1em;
148
+ text-align: left;
149
+ background-color: rgb(56, 82, 102);
150
+ animation: sulod 300ms linear;
151
+ -webkit-animation: sulod 300ms linear;
152
+ }
153
+
154
+ .submenu {
155
+ margin: 0.5em 0 0;
156
+ color: #FFF;
157
+ transition: 0.5s;
158
+ font-size: 90%;
159
+ line-height: 2.2rem;
160
+ padding: 0.5em 0;
161
+ }
162
+
163
+ .spacer .main {
164
+ border: none;
165
+ color: transparent;
166
+ }
167
+
168
+ @keyframes sulod {
169
+ 0% {
170
+ transform: translateX(-100%);
171
+ }
172
+
173
+ 100% {
174
+ transform: translateX(0%);
175
+ }
176
+ }
177
+
178
+ @-moz-keyframes sulod {
179
+ 0% {
180
+ transform: translateX(-100%);
181
+ }
182
+
183
+ 100% {
184
+ transform: translateX(0%);
185
+ }
186
+ }
187
+
188
+ @-webkit-keyframes sulod {
189
+ 0% {
190
+ transform: translateX(-100%);
191
+ }
192
+
193
+ 100% {
194
+ transform: translateX(0%);
195
+ }
196
+ }
197
+
198
+
199
+ .map {
200
+ /* show full screen the iframe */
201
+ position: fixed;
202
+ top: 0;
203
+ left: 0;
204
+ width: 100%;
205
+ height: 100%;
206
+ z-index: -1;
207
+ }
208
+
209
+ .mistral {
210
+ width: 50px;
211
+ height: 42px;
212
+ }
213
+
214
+ .chat-message {
215
+ display: flex;
216
+ align-items: center;
217
+ }
218
+
219
+ .chat-input, .city {
220
+ width: 100%;
221
+ padding: 10px 20px;
222
+ margin: 10px 0;
223
+ box-sizing: border-box;
224
+ border: 3px solid #555;
225
+ border-radius: 4px;
226
+ background-color: #f8f8f8;
227
+ font-size: 16px;
228
+ }
229
+
230
+
231
+ #user-image,
232
+ #mistral-image {
233
+ width: 50px;
234
+ height: 50px;
235
+ margin-right: 10px;
236
+ }
237
+
238
+ .submenu b {
239
+ font-size: 12px;
240
+ color: #56e943;
241
+ /* italic */
242
+ font-style: italic;
243
+ /* set the paragraph space */
244
+ margin-bottom: 10px;
245
+
246
+
247
+ }
248
+
249
+ .button1 {
250
+ background-color: white;
251
+ color: black;
252
+ border: 2px solid #04AA6D;
253
+ /* Green */
254
+ padding: 10px 24px;
255
+ /* Add some padding */
256
+ text-align: center;
257
+ /* Center the text */
258
+ text-decoration: none;
259
+ /* Remove underline */
260
+ display: inline-block;
261
+ font-size: 16px;
262
+ margin: 4px 2px;
263
+ transition-duration: 0.4s;
264
+ /* Add transition */
265
+ cursor: pointer;
266
+ /* Change cursor on hover */
267
+ }
268
+
269
+ .button1:hover {
270
+ background-color: #04AA6D;
271
+ /* Green */
272
+ color: white;
273
+ }
user_location.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"city": "Nancy"}
user_profile.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Emmanuel",
3
+ "age": 30,
4
+ "location": "Nancy",
5
+ "lat": 45.5017,
6
+ "lon": -73.5673
7
+ }
videos.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "1": {
3
+ "name": "Cédric",
4
+ "url": "https://youtu.be/yd9agyhuDp4?list=PLtQ79dzgXtJ_HGJFUb204XbdFGv4LZhRg",
5
+ "location": "Saint-Étienne",
6
+ "lat": 45.4333,
7
+ "lon": 4.4
8
+ },
9
+ "2": {
10
+ "name": "Théo Paul",
11
+ "url": "https://youtu.be/q9yA2UiADXk?si=Q3fHYOnJsqhtbiiw",
12
+ "location": "Nancy",
13
+ "lat": 48.6921,
14
+ "lon": 6.1844
15
+ },
16
+ "3": {
17
+ "name": "Nadège",
18
+ "url": "https://youtu.be/oPcG6hpKNDc?si=yBeJlFqfMbM2f20M",
19
+ "location": "Paris",
20
+ "lat": 48.8566,
21
+ "lon": 2.3522
22
+ },
23
+ "4": {
24
+ "name": "Henri",
25
+ "url": "https://youtu.be/4pgQfTgYUeo?si=XiFJgLmrMLfm1-wj",
26
+ "location": "Lyon",
27
+ "lat": 45.75,
28
+ "lon": 4.85
29
+ }
30
+ }
weather.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+ import datetime
4
+ from pprint import pprint
5
+ from datetime import date
6
+ from dotenv import load_dotenv
7
+ import os
8
+
9
+ load_dotenv()
10
+
11
+
12
+ def get_weather(city, date):
13
+ agro_api_key = os.getenv("AGRO_API_KEY")
14
+ opencage_api_key = os.getenv("OPENCAGE_API_KEY")
15
+ print('==================',agro_api_key,opencage_api_key,'==================')
16
+
17
+ # On récupère les lat et lon de la ville
18
+ geocode_url = f"https://api.opencagedata.com/geocode/v1/json?q={city}&key={opencage_api_key}"
19
+ geocode_response = requests.get(geocode_url)
20
+ geocode_data = geocode_response.json()
21
+
22
+
23
+ latitude = geocode_data['results'][0]['geometry']['lat']
24
+ longitude = geocode_data['results'][0]['geometry']['lng']
25
+
26
+ # On demande la météo pour cette ville
27
+ forecast_url = f"http://api.agromonitoring.com/agro/1.0/weather/forecast?lat={latitude}&lon={longitude}&appid={agro_api_key}"
28
+ forecast_response = requests.get(forecast_url)
29
+
30
+ if forecast_response.status_code == 200:
31
+ forecast_data = forecast_response.json()
32
+ d={}
33
+ for forecast in forecast_data:
34
+ datew = datetime.datetime.fromtimestamp(forecast['dt'])
35
+ temperature = forecast['main']['temp']
36
+ weather_description = forecast['weather'][0]['description']
37
+
38
+ if str(datew).split(' ')[0]==str(date) :
39
+ d[str(datew)]=f"Location : {city}, Temperature: {round(temperature-273.15,2)}°C, Weather: {weather_description}"
40
+ return d, latitude, longitude
41
+ else:
42
+ print(f"Error: {forecast_response.status_code}")
43
+
44
+ # #exemple
45
+ # today = date.today()
46
+ # today = today.strftime("%Y-%m-%d")
47
+ # a=get_weather('Paris',today)
48
+ # print(a)