├── README.md ├── java-samples └── distance-api-client │ ├── README.md │ ├── pom.xml │ └── src │ └── main │ └── java │ └── com │ └── example │ └── WoosmapDistanceApiClient.java ├── jsfiddle-samples ├── driving-directions │ ├── README.MD │ ├── demo.css │ ├── demo.details │ ├── demo.html │ └── demo.js ├── geolocation │ ├── README.MD │ ├── demo.css │ ├── demo.details │ ├── demo.html │ └── demo.js ├── locator-map │ ├── README.MD │ ├── demo.css │ ├── demo.details │ ├── demo.html │ └── demo.js ├── map-infowindow │ ├── README.MD │ ├── demo.css │ ├── demo.details │ ├── demo.html │ └── demo.js ├── map-tiled-view │ ├── README.MD │ ├── demo.css │ ├── demo.details │ ├── demo.html │ └── demo.js ├── search-location │ ├── README.MD │ ├── demo.css │ ├── demo.details │ ├── demo.html │ └── demo.js ├── search-places │ ├── README.MD │ ├── demo.css │ ├── demo.details │ ├── demo.html │ └── demo.js └── search-query │ ├── README.MD │ ├── demo.css │ ├── demo.details │ ├── demo.html │ └── demo.js └── python-samples ├── batchgeocoding ├── README.md ├── google_batch_geocoder.py └── hairdresser_sample_addresses.csv ├── batchgeocoding_woosmap_localities └── woosmap_localities_batch_geocoder.py ├── batchimport ├── README.md ├── batch_import_locations.py └── france_museum_geocoded.csv ├── csv_to_woosmap ├── csv_to_woosmap.py └── foodmarkets.csv ├── excel_to_woosmap ├── excel_to_woosmap.py └── foodmarkets.xlsx ├── googlemybusiness_to_woosmap └── googlemybusiness_to_woosmap.py ├── googleplaces_to_woosmap ├── googleplaces_to_woosmap.py └── search_data.json ├── googlesheet_to_woosmap └── googlesheet_to_woosmap.py ├── woosmap_jsonschema_validation ├── foodmarkets.json └── woosmap_jsonschema_validation.py ├── woosmap_to_geojson └── woosmap_to_geojson.py ├── woosmap_to_woosmap ├── README.md └── woosmap_to_woosmap.py └── woosmapjson_import ├── README.md ├── foodmarkets.json └── woosmapjson_import.py /README.md: -------------------------------------------------------------------------------- 1 | Useful samples to work with woosmap APIs -------------------------------------------------------------------------------- /java-samples/distance-api-client/README.md: -------------------------------------------------------------------------------- 1 | # Woosmap Distance API Client 2 | 3 | This is a simple Java client to interact with the Woosmap Distance API. 4 | 5 | ## Prerequisites 6 | 7 | - Java 11 or later 8 | - Maven 9 | 10 | ## Setup 11 | 12 | 1. Clone the repository. 13 | 2. Replace `YOUR_WOOSMAP_API_KEY` in `WoosmapDistanceApiClient.java` with your actual Woosmap Private API key. 14 | 15 | ## Build and Run 16 | 17 | ```sh 18 | mvn clean install 19 | mvn exec:java -Dexec.mainClass="com.example.WoosmapDistanceApiClient" 20 | ``` 21 | 22 | ## Example 23 | 24 | The client sends a request to the Woosmap Distance API to calculate the distance and duration between Paris and multiple 25 | destinations. Example of output: 26 | 27 | ```shell 28 | [INFO] --- exec:3.0.0:java (default-cli) @ woosmap-distance-api-client --- 29 | Origin 1: 30 | Destination 1: 31 | Distance: 24090.0 meters (24.1 km) 32 | Duration: 1652.0 seconds (28 mins) 33 | Destination 2: 34 | Distance: 15880.0 meters (15.9 km) 35 | Duration: 1095.0 seconds (18 mins) 36 | Destination 3: 37 | Distance: 153263.0 meters (153 km) 38 | Duration: 7331.0 seconds (2 hours 2 mins) 39 | [INFO] ------------------------------------------------------------------------ 40 | ``` 41 | 42 | ## Dependencies 43 | 44 | - Jackson Databind for JSON parsing -------------------------------------------------------------------------------- /java-samples/distance-api-client/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | com.example 6 | woosmap-distance-api-client 7 | 1.0-SNAPSHOT 8 | 9 | 10 | com.fasterxml.jackson.core 11 | jackson-databind 12 | 2.12.3 13 | 14 | 15 | 16 | 17 | 18 | org.codehaus.mojo 19 | exec-maven-plugin 20 | 3.0.0 21 | 22 | 23 | 24 | java 25 | 26 | 27 | 28 | 29 | com.example.WoosmapDistanceApiClient 30 | compile 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /java-samples/distance-api-client/src/main/java/com/example/WoosmapDistanceApiClient.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import java.net.URI; 4 | import java.net.URLEncoder; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | import java.nio.charset.StandardCharsets; 9 | import com.fasterxml.jackson.databind.JsonNode; 10 | import com.fasterxml.jackson.databind.ObjectMapper; 11 | 12 | public class WoosmapDistanceApiClient { 13 | 14 | private static final String API_KEY = "YOUR_WOOSMAP_API_KEY"; 15 | private static final String BASE_URL = "https://api.woosmap.com/distance/distancematrix/json"; 16 | 17 | public static void main(String[] args) { 18 | try { 19 | String origins = "48.836,2.237"; // Example: Paris coordinates 20 | String destinations = "48.709,2.403|48.768,2.338|49.987,2.223"; // Example multiple destinations 21 | String mode = "driving"; // Optional: mode of transport 22 | String language = "en"; // Optional: language 23 | String units = "metric"; // Optional: units 24 | String elements = "duration_distance"; // Optional: elements 25 | String method = "time"; // Optional: method 26 | 27 | String url = String.format("%s?origins=%s&destinations=%s&private_key=%s&mode=%s&language=%s&units=%s&elements=%s&method=%s", 28 | BASE_URL, 29 | URLEncoder.encode(origins, StandardCharsets.UTF_8), 30 | URLEncoder.encode(destinations, StandardCharsets.UTF_8), 31 | URLEncoder.encode(API_KEY, StandardCharsets.UTF_8), 32 | URLEncoder.encode(mode, StandardCharsets.UTF_8), 33 | URLEncoder.encode(language, StandardCharsets.UTF_8), 34 | URLEncoder.encode(units, StandardCharsets.UTF_8), 35 | URLEncoder.encode(elements, StandardCharsets.UTF_8), 36 | URLEncoder.encode(method, StandardCharsets.UTF_8)); 37 | 38 | HttpClient client = HttpClient.newHttpClient(); 39 | HttpRequest request = HttpRequest.newBuilder() 40 | .uri(new URI(url)) 41 | .GET() 42 | .build(); 43 | 44 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 45 | 46 | if (response.statusCode() == 200) { 47 | parseAndPrintResponse(response.body()); 48 | } else { 49 | System.out.println("Error: " + response.statusCode()); 50 | } 51 | } catch (Exception e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | 56 | private static void parseAndPrintResponse(String responseBody) { 57 | try { 58 | ObjectMapper objectMapper = new ObjectMapper(); 59 | JsonNode rootNode = objectMapper.readTree(responseBody); 60 | String status = rootNode.path("status").asText(); 61 | 62 | if (!"OK".equals(status)) { 63 | System.out.println("Error: " + responseBody); 64 | return; 65 | } 66 | 67 | JsonNode rows = rootNode.path("rows"); 68 | 69 | for (int i = 0; i < rows.size(); i++) { 70 | JsonNode row = rows.get(i); 71 | JsonNode elements = row.path("elements"); 72 | System.out.println("Origin " + (i + 1) + ":"); 73 | for (int j = 0; j < elements.size(); j++) { 74 | JsonNode element = elements.get(j); 75 | String elementStatus = element.path("status").asText(); 76 | if ("OK".equals(elementStatus)) { 77 | double distance = element.path("distance").path("value").asDouble(); 78 | String distanceText = element.path("distance").path("text").asText(); 79 | double duration = element.path("duration").path("value").asDouble(); 80 | String durationText = element.path("duration").path("text").asText(); 81 | System.out.println(" Destination " + (j + 1) + ":"); 82 | System.out.println(" Distance: " + distance + " meters (" + distanceText + ")"); 83 | System.out.println(" Duration: " + duration + " seconds (" + durationText + ")"); 84 | } else { 85 | System.out.println(" Destination " + (j + 1) + " Error: " + elementStatus); 86 | } 87 | } 88 | } 89 | } catch (Exception e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /jsfiddle-samples/driving-directions/README.MD: -------------------------------------------------------------------------------- 1 | 2 | In order to view this demo on JSFiddle, open this URL: 3 | https://fiddle.jshell.net/gh/get/library/pure/woosmap/samples/tree/master/jsfiddle-samples/driving-directions/ 4 | 5 | -------------------------------------------------------------------------------- /jsfiddle-samples/driving-directions/demo.css: -------------------------------------------------------------------------------- 1 | #my-map { 2 | height: 500px; 3 | } 4 | 5 | * { 6 | -webkit-box-sizing: border-box; 7 | -moz-box-sizing: border-box; 8 | box-sizing: border-box; 9 | } 10 | 11 | input { 12 | font-size: inherit; 13 | line-height: normal; 14 | } 15 | 16 | #directions-box { 17 | position: absolute; 18 | max-height: 100%; 19 | top: 10px; 20 | left: 10px; 21 | max-width: 350px; 22 | min-width: 250px; 23 | width: 40%; 24 | } 25 | 26 | #inputs, 27 | #errors, 28 | #directions { 29 | width: 100% 30 | } 31 | 32 | #inputs { 33 | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); 34 | display: none; 35 | background-color: white; 36 | } 37 | 38 | #directions { 39 | display: none; 40 | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); 41 | background-color: white; 42 | margin-top: 4px; 43 | max-height: 470px; 44 | } 45 | 46 | #directions-travel-mode-selector { 47 | position: relative; 48 | height: 40px; 49 | } 50 | 51 | #directions-travel-mode-selector .woosmap-form-label { 52 | background-color: #F7F7F7; 53 | } 54 | 55 | #errors { 56 | z-index: 8; 57 | opacity: 0; 58 | padding: 10px; 59 | border-radius: 0 0 3px 3px; 60 | background: rgba(0, 0, 0, .25); 61 | top: 90px; 62 | left: 10px; 63 | } 64 | 65 | /* Basics */ 66 | 67 | .woosmap-directions-inputs, 68 | .woosmap-directions-errors, 69 | .woosmap-directions-routes, 70 | .woosmap-directions-instructions { 71 | font: 300 15px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif; 72 | } 73 | 74 | /* Inputs */ 75 | 76 | .woosmap-directions-origin, 77 | .woosmap-directions-destination { 78 | /*background-color: white;*/ 79 | position: relative; 80 | } 81 | 82 | .woosmap-form-label { 83 | cursor: pointer; 84 | position: absolute; 85 | left: 0; 86 | top: 0; 87 | background: #444; 88 | color: rgba(0, 0, 0, .75); 89 | font-weight: bold; 90 | text-align: center; 91 | padding: 10px; 92 | line-height: 20px; 93 | } 94 | 95 | .woosmap-directions-origin .woosmap-form-label { 96 | background-color: #1badee; 97 | } 98 | 99 | .woosmap-travel-mode-option { 100 | cursor: pointer; 101 | top: 0; 102 | color: rgba(0, 0, 0, .75); 103 | text-align: center; 104 | padding: 10px; 105 | line-height: 20px; 106 | } 107 | 108 | .woosmap-directions-inputs input { 109 | width: 100%; 110 | border: 0; 111 | background-color: transparent; 112 | height: 40px; 113 | margin: 0; 114 | color: rgba(0, 0, 0, .5); 115 | padding: 10px 10px 10px 50px; 116 | font-weight: 300; 117 | } 118 | 119 | .woosmap-directions-inputs input:focus { 120 | color: rgba(0, 0, 0, .75); 121 | outline: 0; 122 | box-shadow: none; 123 | outline: thin dotted; 124 | } 125 | 126 | .woosmap-directions-origin input { 127 | border-top: 1px solid rgba(0, 0, 0, .1); 128 | } 129 | 130 | .woosmap-directions-destination input { 131 | border-top: 1px solid rgba(0, 0, 0, .1); 132 | } 133 | 134 | .woosmap-directions-reverse-input { 135 | position: absolute; 136 | background: white; 137 | left: 50px; 138 | top: 70px; 139 | cursor: pointer; 140 | } 141 | 142 | .woosmap-directions-inputs .woosmap-close-icon { 143 | opacity: .5; 144 | position: absolute; 145 | right: 5px; 146 | top: 10px; 147 | cursor: pointer; 148 | } 149 | 150 | input:not(:valid) + .woosmap-close-icon { 151 | display: none; 152 | } 153 | 154 | .woosmap-close-icon:hover { 155 | opacity: .75; 156 | } 157 | 158 | /* Errors */ 159 | 160 | .woosmap-directions-error { 161 | color: white; 162 | display: inline-block; 163 | padding: 0 5px; 164 | } 165 | 166 | /* Routes */ 167 | 168 | .woosmap-route-container { 169 | padding: 6px; 170 | box-shadow: -1px 5px 10px -4px #aaa6a0; 171 | } 172 | 173 | .woosmap-route-info-container { 174 | border-bottom: 1px solid rgb(230, 230, 230); 175 | } 176 | 177 | .woosmap-route-info-container.hide { 178 | border-bottom: none; 179 | } 180 | 181 | .woosmap-route-short-duration { 182 | color: rgb(152, 152, 152); 183 | font-size: small 184 | } 185 | 186 | .woosmap-route-traffic-duration { 187 | color: #1badee; 188 | } 189 | 190 | .woosmap-route-details { 191 | font-size: small; 192 | color: #3b8bba; 193 | margin-bottom: 10px; 194 | } 195 | 196 | .woosmap-route-details:hover { 197 | text-decoration: underline; 198 | cursor: pointer; 199 | } 200 | 201 | .route-summary-upper-panel.hide { 202 | cursor: pointer; 203 | } 204 | 205 | /* Mail View */ 206 | 207 | .woosmap-mail-container { 208 | padding: 6px; 209 | box-shadow: -1px 5px 10px -4px #aaa6a0; 210 | } 211 | 212 | .woosmap-mobile-form-menu { 213 | cursor: pointer; 214 | margin: 2px 0px 3px -3px; 215 | } 216 | 217 | .woosmap-mobile-icon { 218 | background-image: url('https://developers.woosmap.com/img/mobile-icon.png'); 219 | -webkit-background-size: 20px 20px; 220 | background-size: 20px 20px; 221 | background-repeat: no-repeat; 222 | content: ''; 223 | display: inline-block; 224 | vertical-align: top; 225 | width: 20px; 226 | height: 20px; 227 | cursor: pointer; 228 | margin-top: 1px; 229 | background-color: white; 230 | } 231 | 232 | .woosmap-mobile-link-container { 233 | float: right; 234 | margin: 3px; 235 | } 236 | 237 | /* Instructions */ 238 | 239 | .instructions-header { 240 | background-color: #1badee; 241 | } 242 | 243 | #instructions-steps { 244 | overflow-y: scroll; 245 | overflow-x: hidden; 246 | max-height: 445px; 247 | padding-left: 5px; 248 | padding-right: 5px; 249 | font-size: 13px; 250 | } 251 | 252 | .woosmap-instructions-title { 253 | vertical-align: middle; 254 | padding-left: 10px; 255 | color: white; 256 | } 257 | 258 | .close-instructions-button { 259 | background-image: url('https://developers.woosmap.com/img/close-x.png'); 260 | cursor: pointer; 261 | background-color: #1badee; 262 | width: 24px; 263 | height: 24px; 264 | float: right; 265 | } 266 | 267 | .woosmap-directions-steps { 268 | position: relative; 269 | list-style: none; 270 | margin: 0; 271 | padding: 0; 272 | } 273 | 274 | .woosmap-directions-step { 275 | position: relative; 276 | color: rgba(255, 255, 255, .75); 277 | cursor: pointer; 278 | padding: 20px 20px 20px 40px; 279 | font-size: 20px; 280 | line-height: 25px; 281 | } 282 | 283 | .woosmap-directions-step-distance { 284 | color: rgba(255, 255, 255, .5); 285 | position: absolute; 286 | padding: 5px 10px; 287 | font-size: 12px; 288 | left: 30px; 289 | bottom: -15px; 290 | } 291 | 292 | .woosmap-directions-step:hover { 293 | color: white; 294 | } 295 | 296 | .woosmap-directions-step:after { 297 | content: ""; 298 | position: absolute; 299 | top: 50px; 300 | bottom: -20px; 301 | border-left: 2px dotted rgba(255, 255, 255, .2); 302 | left: 20px; 303 | } 304 | 305 | .woosmap-directions-step:last-child:after, 306 | .woosmap-directions-step:last-child .woosmap-directions-step-distance { 307 | display: none; 308 | } 309 | 310 | /* icons */ 311 | 312 | .woosmap-geolocation-icon { 313 | background-image: url('https://developers.woosmap.com/img/location.png'); 314 | -webkit-background-size: 280px 20px; 315 | background-size: 20px 20px; 316 | background-repeat: no-repeat; 317 | margin: 0; 318 | content: ''; 319 | display: inline-block; 320 | vertical-align: top; 321 | width: 20px; 322 | height: 20px; 323 | } 324 | 325 | .woosmap-travel-mode { 326 | padding: 0 0 0 40px; 327 | background: white; 328 | text-align: center; 329 | } 330 | 331 | .woosmap-directions-icon { 332 | background-image: url('https://developers.woosmap.com/img/woosmap.directions.png'); 333 | -webkit-background-size: 280px 20px; 334 | background-size: 280px 20px; 335 | background-repeat: no-repeat; 336 | margin: 0; 337 | content: ''; 338 | display: inline-block; 339 | vertical-align: top; 340 | width: 20px; 341 | height: 20px; 342 | } 343 | 344 | .woosmap-directions-instructions .woosmap-directions-icon { 345 | position: absolute; 346 | left: 10px; 347 | top: 25px; 348 | margin: auto; 349 | } 350 | 351 | .woosmap-depart-icon { 352 | background-position: -160px 0; 353 | } 354 | 355 | .woosmap-arrive-icon { 356 | background-position: -200px 0; 357 | } 358 | 359 | .woosmap-close-icon { 360 | background-position: -220px 0; 361 | } 362 | 363 | .woosmap-reverse-icon { 364 | background-position: -240px 0; 365 | } 366 | 367 | .woosmap-travel-mode-option { 368 | background-color: white; 369 | } 370 | 371 | .woosmap-travel-mode-icon { 372 | background-image: url('https://developers.woosmap.com/img/driving-sprite.png'); 373 | -webkit-background-size: 20px 276px; 374 | background-size: 20px 276px; 375 | background-repeat: no-repeat; 376 | margin: 0; 377 | content: ''; 378 | display: inline-block; 379 | vertical-align: top; 380 | width: 20px; 381 | height: 20px; 382 | } 383 | 384 | .woosmap-driving-icon { 385 | background-position: 0 -40px; 386 | } 387 | 388 | .woosmap-walking-icon { 389 | background-position: 0 -120px; 390 | } 391 | 392 | .woosmap-bicycling-icon { 393 | background-position: 0 -160px; 394 | } 395 | 396 | .selected .woosmap-driving-icon { 397 | background-position: 0 -60px; 398 | } 399 | 400 | .selected .woosmap-walking-icon { 401 | background-position: 0 -140px; 402 | } 403 | 404 | .selected .woosmap-bicycling-icon { 405 | background-position: 0 -180px; 406 | } 407 | 408 | .woosmap-travel-mode-option.selected { 409 | box-shadow: inset 0 -2px 0px 0px #3983de; 410 | } 411 | 412 | /*------------override google maps style----------*/ 413 | .adp-warnbox { 414 | display: none; 415 | } 416 | 417 | .adp-placemark { 418 | border: none; 419 | background: #FFF; 420 | } 421 | 422 | #adp-placemark, .adp-placemark { 423 | font-weight: bold !important; 424 | } 425 | 426 | .adp-substep { 427 | max-width: 153px; 428 | } 429 | 430 | #directions .adp-placemark img { 431 | width: 30px; 432 | height: 44px; 433 | margin-right: 5px; 434 | } 435 | 436 | #directions #adp-placemark img { 437 | content: url('https://developers.woosmap.com/img/markers/start.png') !important; 438 | } 439 | 440 | #directions .adp-placemark:last-child img { 441 | content: url('https://developers.woosmap.com/img/markers/end.png') !important; 442 | } 443 | 444 | #map-container { 445 | position: relative; 446 | } 447 | 448 | /*grids*/ 449 | .pure-g { 450 | letter-spacing: -.31em; 451 | text-rendering: optimizespeed; 452 | display: -webkit-flex; 453 | -webkit-flex-flow: row wrap; 454 | display: -ms-flexbox; 455 | -ms-flex-flow: row wrap; 456 | -ms-align-content: flex-start; 457 | -webkit-align-content: flex-start; 458 | align-content: flex-start; 459 | } 460 | 461 | .pure-g [class *="pure-u"] { 462 | font-family: "Open Sans", "Helvetica Neue", Arial, Helvetica, Verdana, sans-serif; 463 | font-weight: normal; 464 | letter-spacing: 0.01em; 465 | } 466 | 467 | .pure-u-1, .pure-u-1-3 { 468 | display: inline-block; 469 | zoom: 1; 470 | letter-spacing: normal; 471 | word-spacing: normal; 472 | vertical-align: top; 473 | text-rendering: auto; 474 | } 475 | 476 | .pure-u-1 { 477 | width: 100%; 478 | } 479 | 480 | .pure-u-1-3 { 481 | width: 33.3333%; 482 | *width: 33.3023% 483 | } 484 | 485 | .pure-form input[type=text], .pure-form input[type=email] { 486 | padding: .5em .6em; 487 | display: inline-block; 488 | border: 1px solid #ccc; 489 | box-shadow: inset 0 1px 3px #ddd; 490 | border-radius: 4px; 491 | vertical-align: middle; 492 | -webkit-box-sizing: border-box; 493 | -moz-box-sizing: border-box; 494 | box-sizing: border-box 495 | } 496 | 497 | .pure-form input:not([type]) { 498 | padding: .5em .6em; 499 | display: inline-block; 500 | border: 1px solid #ccc; 501 | box-shadow: inset 0 1px 3px #ddd; 502 | border-radius: 4px; 503 | -webkit-box-sizing: border-box; 504 | -moz-box-sizing: border-box; 505 | box-sizing: border-box 506 | } 507 | 508 | .pure-form input[type=text]:focus, .pure-form input[type=email]:focus { 509 | outline: 0; 510 | border-color: #129FEA 511 | } 512 | 513 | .pure-form input:not([type]):focus { 514 | outline: 0; 515 | border-color: #129FEA 516 | } 517 | 518 | .pure-form input[type=text][disabled], .pure-form input[type=email][disabled] { 519 | cursor: not-allowed; 520 | background-color: #eaeded; 521 | color: #cad2d3 522 | } 523 | 524 | .pure-form input:not([type])[disabled] { 525 | cursor: not-allowed; 526 | background-color: #eaeded; 527 | color: #cad2d3 528 | } 529 | 530 | .pure-form input[readonly] { 531 | background-color: #eee; 532 | color: #777; 533 | border-color: #ccc 534 | } 535 | 536 | .pure-form input:focus:invalid { 537 | color: #b94a48; 538 | border-color: #e9322d 539 | } 540 | 541 | .pure-form label { 542 | margin: .5em 0 .2em 543 | } 544 | 545 | @media only screen and (max-width: 480px) { 546 | .pure-form button[type=submit] { 547 | margin: .7em 0 0 548 | } 549 | 550 | .pure-form input:not([type]), .pure-form input[type=text], .pure-form input[type=email], .pure-form label { 551 | margin-bottom: .3em; 552 | display: block 553 | } 554 | 555 | .pure-group input:not([type]), .pure-group input[type=text], .pure-group input[type=email] { 556 | margin-bottom: 0 557 | } 558 | } 559 | 560 | ::-webkit-scrollbar { 561 | width: 5px; 562 | height: 5px; 563 | } 564 | 565 | ::-webkit-scrollbar-thumb { 566 | background-color: #d1d1d1; 567 | } 568 | 569 | ::-webkit-scrollbar-track { 570 | background-color: #F7F7F7; 571 | } -------------------------------------------------------------------------------- /jsfiddle-samples/driving-directions/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: DrivingDirections - Woosmap Javascript API Driving Directions Demo 3 | description: jsFiddle demo that allow the use of Google maps Driving Directions using Woosmap Javascript API. 4 | authors: 5 | - Woosmap DevTeam 6 | ... -------------------------------------------------------------------------------- /jsfiddle-samples/driving-directions/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 51 | 102 | 119 | 120 |
121 |
122 |
123 |
124 |
-------------------------------------------------------------------------------- /jsfiddle-samples/driving-directions/demo.js: -------------------------------------------------------------------------------- 1 | var projectKey = '12345678'; 2 | var markersStyle = { 3 | rules: [ 4 | { 5 | type: 'drive', 6 | icon: {url: 'https://images.woosmap.com/marker_drive.svg', scaledSize: {width: 36, height: 48}}, 7 | selectedIcon: {url: 'https://images.woosmap.com/marker_drive_selected.svg', scaledSize: {width: 46, height: 60}} 8 | } 9 | ], 10 | default: { 11 | icon: {url: 'https://images.woosmap.com/marker_default.svg', scaledSize: {width: 36, height: 48}}, 12 | selectedIcon: {url: 'https://images.woosmap.com/marker_selected.svg', scaledSize: {width: 46, height: 60}} 13 | } 14 | }; 15 | var tilesStyle = { 16 | color: '#383838', 17 | size: 11, 18 | minSize: 6, 19 | typeRules: [{ 20 | type: 'drive', 21 | color: '#82a859' 22 | }] 23 | }; 24 | 25 | //this function is called when loader finished the API loading 26 | function woosmap_main() { 27 | var loader = new woosmap.MapsLoader(); 28 | var dataSource = new woosmap.DataSource(); 29 | loader.load(function () { 30 | 31 | /******** usefull function ********/ 32 | function closeRouteContainer() { 33 | woosmap.$('.route-summary-upper-panel').addClass('hide'); 34 | woosmap.$('.route-summary-lower-panel').hide(); 35 | woosmap.$('.woosmap-route-details').hide(); 36 | woosmap.$('.woosmap-route-info-container').addClass('hide'); 37 | } 38 | 39 | function displayRouteContainer(container) { 40 | var $container = woosmap.$(container); 41 | $container.find('.route-summary-lower-panel').show(); 42 | $container.find('.woosmap-route-details').show(); 43 | $container.find('.woosmap-route-info-container').removeClass('hide'); 44 | $container.find('.route-summary-upper-panel').removeClass('hide'); 45 | } 46 | 47 | function makeMarker(position, icon, title) { 48 | directionsMarkers.push(new google.maps.Marker({ 49 | position: position, 50 | map: map, 51 | icon: icon, 52 | title: title 53 | })); 54 | } 55 | 56 | function cleanMarker() { 57 | woosmap.$.each(directionsMarkers, function (index, marker) { 58 | marker.setMap(null); 59 | }); 60 | directionsMarkers = []; 61 | } 62 | 63 | /*********************************/ 64 | 65 | var map = new google.maps.Map(woosmap.$('#my-map')[0], { 66 | center: { 67 | lat: 45, 68 | lng: 2 69 | }, 70 | zoom: 5, 71 | disableDefaultUI: true 72 | }); 73 | 74 | var mapView = new woosmap.TiledView(map, { 75 | style: markersStyle, 76 | tileStyle: tilesStyle 77 | }); 78 | 79 | 80 | /**** directions renderers options ****/ 81 | var newPolylineOption = { 82 | strokeColor: '#1badee', 83 | strokeOpacity: 1.0, 84 | strokeWeight: 4, 85 | icons: ['https://developers.woosmap.com/img/markers/marker.png'] 86 | }; 87 | 88 | // Start/Finish icons 89 | var icons = { 90 | start: 'https://developers.woosmap.com/img/markers/start.png', 91 | end: 'https://developers.woosmap.com/img/markers/end.png' 92 | }; 93 | var directionRendererOptions = { 94 | suppressMarkers: true, 95 | suppressInfoWindows: true, 96 | polylineOptions: newPolylineOption 97 | }; 98 | 99 | var googleDirectionsRequestOptions = { 100 | provideRouteAlternatives: true, 101 | durationInTraffic: true 102 | }; 103 | /***************************************/ 104 | 105 | var directionsMarkers = []; 106 | var navigatorGeolocation = new woosmap.location.LocationProvider(); 107 | var travelModeSelector = new woosmap.ui.TravelModeSelector(woosmap.$('#travel-mode-selector-template').html()); 108 | var originDestinationInput = new woosmap.ui.OriginDestinationInput(woosmap.$('#directions-origin-destination-template').html(), { 109 | 'geolocText': 'Ma Position' 110 | }); 111 | var directionsProvider = new woosmap.location.DirectionsProvider(directionRendererOptions, googleDirectionsRequestOptions); 112 | var mailView = new woosmap.ui.MailView(woosmap.$('#directions-mail-input-template').html()); 113 | var locationProvider = new woosmap.location.LocationProvider(); 114 | var store_id = ''; 115 | var directionsRestorer = new woosmap.utils.MVCObject(); 116 | directionsRestorer.location = null; 117 | directionsRestorer.location_changed = function () { 118 | var self = this; 119 | if (store_id) { 120 | dataSource.getStoreById(store_id, function (data) { 121 | originDestinationInput.set('selectedStore', data); 122 | originDestinationInput.set('location', self.get('location')); 123 | }); 124 | } 125 | }; 126 | 127 | var directionsResultsDisplayer = new woosmap.ui.DirectionsResultsDisplayer(map, woosmap.$('#directions-summary-template').html(), 128 | function () { 129 | //this function is called when directionResultsDisplayer finished to display renderers 130 | directionsResultsDisplayer.displayRouteOnMap(0); 131 | directionsResultsDisplayer.displayRouteSteps(0); 132 | woosmap.$("#directions").show(); 133 | var computedDirections = directionsResultsDisplayer.get("directionsRenderers")[0].getDirections(); 134 | var leg = computedDirections.routes[0].legs[0]; 135 | cleanMarker(); 136 | makeMarker(leg.start_location, icons.start, "Start"); 137 | makeMarker(leg.end_location, icons.end, 'End'); 138 | 139 | closeRouteContainer(); 140 | displayRouteContainer(woosmap.$('.woosmap-route-container')[0]); 141 | 142 | woosmap.$('.woosmap-route-container').click(function () { 143 | closeRouteContainer(); 144 | displayRouteContainer(this); 145 | directionsResultsDisplayer.cleanMapFromRoutes(); 146 | directionsResultsDisplayer.cleanRouteSteps(); 147 | directionsResultsDisplayer.displayRouteOnMap(woosmap.$(this).find('.woosmap-show-steps').data('renderer-index')); 148 | directionsResultsDisplayer.displayRouteSteps(woosmap.$(this).find('.woosmap-show-steps').data('renderer-index')); 149 | }); 150 | 151 | woosmap.$('.woosmap-route-details').click(function () { 152 | woosmap.$('#instructions').show(); 153 | woosmap.$('#routes').hide(); 154 | woosmap.$('#inputs').hide(); 155 | woosmap.$('#instructions-mail').hide(); 156 | woosmap.$('#directions').css('top', '5px'); 157 | woosmap.$('#directions').css('bottom', '5px'); 158 | }); 159 | 160 | woosmap.$('#close-instructions-button').click(function () { 161 | woosmap.$('#instructions').hide(); 162 | woosmap.$('#routes').show(); 163 | woosmap.$('#inputs').show(); 164 | woosmap.$('#instructions-mail').show(); 165 | woosmap.$('#directions').css('top', '135px'); 166 | woosmap.$('#directions').css('bottom', ''); 167 | }); 168 | } 169 | ); 170 | 171 | originDestinationInput.bindTo('selectedStore', mapView); 172 | directionsProvider.bindTo('selectedTravelMode', travelModeSelector); 173 | directionsProvider.bindTo('originDestination', originDestinationInput); 174 | directionsResultsDisplayer.bindTo('directionsSummaries', directionsProvider); 175 | directionsResultsDisplayer.bindTo('directionsRenderers', directionsProvider); 176 | directionsResultsDisplayer.bindTo('directionsLink', directionsProvider); 177 | originDestinationInput.bindTo('location', navigatorGeolocation); 178 | mailView.bindTo('selectedStore', mapView); 179 | directionsRestorer.bindTo('location', locationProvider); 180 | 181 | function _update_mail_status(text, color) { 182 | var $mailStatusDiv = woosmap.$('#mail-status'); 183 | $mailStatusDiv.html(text).css('color', color); 184 | $mailStatusDiv.show(); 185 | setTimeout(function () { 186 | $mailStatusDiv.hide(1000); 187 | }, 3000); 188 | } 189 | 190 | mailView.delegate = { 191 | mailSent: function () { 192 | _update_mail_status('Email envoyé', 'green'); 193 | woosmap.$('.woosmap-mail-input').val(""); 194 | }, 195 | mailError: function () { 196 | _update_mail_status('Erreur', 'red'); 197 | }, 198 | mailSending: function () { 199 | _update_mail_status('Envoi en cours', '#1badee'); 200 | } 201 | }; 202 | 203 | woosmap.$('#map-container').append(originDestinationInput.getODContainer()); 204 | woosmap.$('#routes').append(directionsResultsDisplayer.getRoutesContainer()); 205 | woosmap.$('#instructions-steps').append(directionsResultsDisplayer.getStepsContainer()); 206 | woosmap.$('#directions-travel-mode-selector').append(travelModeSelector.getSelectorContainer()); 207 | 208 | woosmap.$('#instructions-mail').empty().append(mailView.getContainer()); 209 | 210 | if (new woosmap.DeviceDetector().getDeviceType() == 'mobile') { 211 | woosmap.$('.woosmap-mail-container').hide(); 212 | } else { 213 | woosmap.$('.woosmap-mobile-form-menu').click(function () { 214 | woosmap.$('.woosmap-mobile-form').toggle(); 215 | }); 216 | } 217 | 218 | woosmap.$(".woosmap-directions-origin .woosmap-close-icon").click(function () { 219 | woosmap.$("#woosmap-directions-origin-input").val(''); 220 | }); 221 | 222 | woosmap.$(".woosmap-directions-destination .woosmap-close-icon").click(function () { 223 | woosmap.$("#woosmap-directions-destination-input").val(''); 224 | }); 225 | 226 | woosmap.$('#directions-travel-mode-selector .woosmap-travel-mode-option').click(function () { 227 | woosmap.$('#directions-travel-mode-selector .woosmap-travel-mode-option').removeClass('selected'); 228 | woosmap.$(this).addClass('selected'); 229 | }); 230 | 231 | woosmap.$('.geolocation-button').click(function () { 232 | navigatorGeolocation.askForLocation(navigator.geolocation); 233 | }); 234 | 235 | google.maps.event.addListener(map, 'click', function (event) { 236 | originDestinationInput.set('location', { 237 | 'lat': event.latLng.lat(), 238 | 'lng': event.latLng.lng() 239 | }); 240 | }); 241 | window.setTimeout(function () { 242 | store_id = top.location.search.split('store_id=')[1] ? top.location.search.split('store_id=')[1].replace('&', '') : ''; 243 | if (store_id) { 244 | dataSource.getStoreById(store_id, function (data) { 245 | originDestinationInput.set('selectedStore', data); 246 | originDestinationInput.set('location', self.get('location')); 247 | }); 248 | locationProvider.askForLocation(navigator.geolocation); 249 | } 250 | }, 1000); 251 | woosmap.$("#inputs").show(); 252 | }); 253 | } 254 | 255 | document.addEventListener("DOMContentLoaded", function (event) { 256 | WoosmapLoader.load('latest', projectKey, woosmap_main); 257 | }); 258 | -------------------------------------------------------------------------------- /jsfiddle-samples/geolocation/README.MD: -------------------------------------------------------------------------------- 1 | 2 | In order to view this demo on JSFiddle, open this URL: 3 | https://fiddle.jshell.net/gh/get/library/pure/woosmap/samples/tree/master/jsfiddle-samples/geolocation/ 4 | 5 | -------------------------------------------------------------------------------- /jsfiddle-samples/geolocation/demo.css: -------------------------------------------------------------------------------- 1 | .bg { 2 | background: white; 3 | padding: 5px; 4 | } 5 | 6 | .nearest-store-map { 7 | height: 250px; 8 | width: 76%; 9 | float: left 10 | } 11 | 12 | .nearest-store-button { 13 | width: 100%; 14 | } 15 | 16 | .nearest-store-button-div { 17 | float: right; 18 | width: 23%; 19 | } 20 | 21 | #closest-store-info { 22 | border: 1px solid grey; 23 | display: none; 24 | width: 50%; 25 | } 26 | 27 | #html5-geolocation-button { 28 | width: 100%; 29 | margin-top: 10px; 30 | } 31 | 32 | #store-info { 33 | margin-top: 52px; 34 | } 35 | 36 | ::-webkit-scrollbar { 37 | width: 5px; 38 | height: 5px; 39 | } 40 | 41 | ::-webkit-scrollbar-thumb { 42 | background-color: #d1d1d1; 43 | } 44 | 45 | ::-webkit-scrollbar-track { 46 | background-color: #F7F7F7; 47 | } 48 | 49 | /*pure-css*/ 50 | 51 | .pure-button { 52 | /* Structure */ 53 | display: inline-block; 54 | zoom: 1; 55 | line-height: normal; 56 | white-space: nowrap; 57 | vertical-align: middle; 58 | text-align: center; 59 | cursor: pointer; 60 | -webkit-user-drag: none; 61 | -webkit-user-select: none; 62 | -moz-user-select: none; 63 | -ms-user-select: none; 64 | user-select: none; 65 | -webkit-box-sizing: border-box; 66 | -moz-box-sizing: border-box; 67 | box-sizing: border-box; 68 | } 69 | 70 | .pure-button { 71 | font-family: inherit; 72 | font-size: 100%; 73 | padding: 0.5em 1em; 74 | color: #444; /* rgba not supported (IE 8) */ 75 | color: rgba(0, 0, 0, 0.80); /* rgba supported */ 76 | border: 1px solid #999; /*IE 6/7/8*/ 77 | border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/ 78 | background-color: #E6E6E6; 79 | text-decoration: none; 80 | border-radius: 2px; 81 | } 82 | 83 | .pure-button-hover, 84 | .pure-button:hover, 85 | .pure-button:focus { 86 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0); 87 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0, 0, 0, 0.05)), to(rgba(0, 0, 0, 0.10))); 88 | background-image: -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)); 89 | background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.05) 0%, rgba(0, 0, 0, 0.10)); 90 | background-image: -o-linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)); 91 | background-image: linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)); 92 | } 93 | 94 | .pure-button:focus { 95 | outline: 0; 96 | } 97 | 98 | .pure-button-active, 99 | .pure-button:active { 100 | box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 0 6px rgba(0, 0, 0, 0.20) inset; 101 | border-color: #000 \9; 102 | } 103 | 104 | hr { 105 | border: 0; 106 | height: 0; 107 | border-top: 1px solid rgba(0, 0, 0, 0.1); 108 | border-bottom: 1px solid rgba(255, 255, 255, 0.3); 109 | } 110 | -------------------------------------------------------------------------------- /jsfiddle-samples/geolocation/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: Geolocation - Woosmap Javascript API Geolocation Demo 3 | description: jsFiddle demo that allow the geolocation of a user using Woosmap Javascript API. 4 | authors: 5 | - Woosmap DevTeam 6 | ... -------------------------------------------------------------------------------- /jsfiddle-samples/geolocation/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 |
7 |
8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 |

21 | 25 | 29 | ... 30 |

31 |
32 | 33 | 34 |

35 | 43 | 44 |
-------------------------------------------------------------------------------- /jsfiddle-samples/geolocation/demo.js: -------------------------------------------------------------------------------- 1 | var projectKey = '12345678'; 2 | var markersStyle = { 3 | rules: [ 4 | { 5 | type: 'drive', 6 | icon: {url: 'https://images.woosmap.com/marker_drive.svg', scaledSize: {width: 36, height: 48}}, 7 | selectedIcon: {url: 'https://images.woosmap.com/marker_drive_selected.svg', scaledSize: {width: 46, height: 60}} 8 | } 9 | ], 10 | default: { 11 | icon: {url: 'https://images.woosmap.com/marker_default.svg', scaledSize: {width: 36, height: 48}}, 12 | selectedIcon: {url: 'https://images.woosmap.com/marker_selected.svg', scaledSize: {width: 46, height: 60}} 13 | } 14 | }; 15 | var tilesStyle = { 16 | color: '#383838', 17 | size: 11, 18 | minSize: 6, 19 | typeRules: [{ 20 | type: 'drive', 21 | color: '#82a859' 22 | }] 23 | }; 24 | 25 | //this function is called when loader finished the API loading 26 | function woosmap_main() { 27 | var loader = new woosmap.MapsLoader(); 28 | var dataSource = new woosmap.DataSource(); 29 | loader.load(function () { 30 | var map = new google.maps.Map(woosmap.$('#nearest-store-map')[0], { 31 | center: {lat: 46, lng: 3}, 32 | zoom: 5 33 | }); 34 | var mapView = new woosmap.TiledView(map, {style: markersStyle, tileStyle: tilesStyle}); 35 | 36 | var locationProvider = new woosmap.location.LocationProvider(); 37 | var locationProviderMap = new woosmap.location.LocationProvider(); 38 | var zipCodeProvider = new woosmap.ZipCodeProvider(); 39 | var zipCodeWatcher = new woosmap.utils.MVCObject(); 40 | 41 | var template = "{{name}}
{{address.zipcode}} {{address.city}}
{{contact.phone}}
{{distance}} km"; 42 | var storesInformationTemplateRenderer = new woosmap.TemplateRenderer(template); 43 | var storesInformationDisplayer = new woosmap.utils.MVCObject(); 44 | storesInformationDisplayer.stores = null; 45 | storesInformationDisplayer.stores_changed = function () { 46 | var properties = this.get('stores')[0].properties; 47 | properties.distance = Math.round(properties.distance / 1000); 48 | woosmap.$('#store-info').html(storesInformationTemplateRenderer.render(properties)); 49 | }; 50 | storesInformationDisplayer.bindTo('stores', mapView); 51 | 52 | var nearbyStoresSource = new woosmap.location.NearbyStoresSource(dataSource, 1); 53 | nearbyStoresSource.bindTo('stores', mapView); 54 | mapView.bindTo('location', locationProviderMap); 55 | 56 | mapView.marker.setOptions({ 57 | draggable: true, 58 | icon: {url: 'https://developers.woosmap.com/img/markers/geolocated.png'} 59 | }); 60 | 61 | nearbyStoresSource.bindTo('location', locationProviderMap); 62 | zipCodeProvider.bindTo('location', locationProvider); 63 | zipCodeWatcher.bindTo('zipcode', zipCodeProvider); 64 | 65 | zipCodeWatcher.zipcode_changed = function () { 66 | woosmap.$('#geoloc-zipcode-result').html(zipCodeProvider.getZipCode()); 67 | }; 68 | 69 | woosmap.$("#geoloc-zipcode-ip").click(function () { 70 | woosmap.$('#geoloc-zipcode-result').html('looking for ...'); 71 | woosmap.$('#geoloc-zipcode-result').html(zipCodeProvider.getZipCode()); 72 | }); 73 | woosmap.$("#geoloc-zipcode-optin").click(function () { 74 | locationProvider.askForLocation(navigator.geolocation); 75 | }); 76 | 77 | woosmap.$("#ip-geolocation-button").click(function () { 78 | locationProviderMap.askForLocation(); 79 | }); 80 | 81 | woosmap.$("#html5-geolocation-button").click(function () { 82 | locationProviderMap.askForLocation(navigator.geolocation); 83 | }); 84 | 85 | /*---------- DistanceProvider --------------*/ 86 | var template = woosmap.$('#closest-store-template').html(); 87 | var anotherLocationProvider = new woosmap.location.LocationProvider(); 88 | var anotherNearbyStoresSource = new woosmap.location.NearbyStoresSource(dataSource, 10); 89 | var distanceProvider = new woosmap.location.DistanceProvider(); 90 | var closestStoreTemplateRenderer = new woosmap.TemplateRenderer(template); 91 | var closestStoreDisplayer = new woosmap.utils.MVCObject(); 92 | closestStoreDisplayer.stores = null; 93 | closestStoreDisplayer.stores_changed = function () { 94 | distanceProvider.updateStoresDistanceWithGoogle(this.get('stores'), function (updated_stores) { 95 | var $storesDiv = woosmap.$('#closest-store-info'); 96 | var store_properties = updated_stores[0].properties; 97 | store_properties.distance = store_properties.distance / 1000; 98 | store_properties.duration = Math.round(store_properties.duration / 60); 99 | $storesDiv.html(closestStoreTemplateRenderer.render(store_properties)); 100 | $storesDiv.show(); 101 | }, 'duration'); 102 | }; 103 | 104 | 105 | closestStoreDisplayer.bindTo('stores', anotherNearbyStoresSource); 106 | anotherNearbyStoresSource.bindTo('location', anotherLocationProvider); 107 | distanceProvider.bindTo('location', anotherLocationProvider); 108 | woosmap.$('#update-stores-distance').click(function () { 109 | anotherLocationProvider.askForLocation(navigator.geolocation); 110 | }); 111 | 112 | 113 | }); 114 | } 115 | 116 | document.addEventListener("DOMContentLoaded", function (event) { 117 | WoosmapLoader.load('latest', projectKey, woosmap_main); 118 | }); 119 | -------------------------------------------------------------------------------- /jsfiddle-samples/locator-map/README.MD: -------------------------------------------------------------------------------- 1 | 2 | In order to view this demo on JSFiddle, open this URL: 3 | https://fiddle.jshell.net/gh/get/library/pure/woosmap/samples/tree/master/jsfiddle-samples/locator-map/ 4 | 5 | -------------------------------------------------------------------------------- /jsfiddle-samples/locator-map/demo.css: -------------------------------------------------------------------------------- 1 | #my-map { 2 | height: 500px; 3 | } 4 | 5 | .locator-container { 6 | font-size: 15px; 7 | line-height: 1.5em; 8 | color: #555; 9 | } 10 | 11 | .sidebar { 12 | height: 500px; 13 | overflow: hidden; 14 | border: 1px solid #EEE; 15 | border-right: none; 16 | } 17 | 18 | .locator-heading { 19 | background: #fff; 20 | border-bottom: 1px solid #eee; 21 | height: 36px; 22 | line-height: 36px; 23 | padding: 0 10px; 24 | } 25 | 26 | .locator-heading h2 { 27 | font-size: 20px; 28 | margin: 0; 29 | font-weight: 400; 30 | color: #333; 31 | } 32 | 33 | .listings { 34 | height: 460px; 35 | padding-bottom: 36px; 36 | background-color: #FFF; 37 | color:#555; 38 | } 39 | 40 | .woosmap-tableview-container, .card_container { 41 | max-height: 100%; 42 | overflow-y: scroll; 43 | overflow-x: hidden; 44 | } 45 | 46 | .item { 47 | display: block; 48 | border-bottom: 1px solid #eee; 49 | padding: 10px; 50 | text-decoration: none; 51 | cursor: pointer; 52 | } 53 | 54 | .item .title { 55 | display: block; 56 | color: #4d9da9; 57 | font-weight: 500; 58 | } 59 | 60 | .item .title small { 61 | font-weight: 300; 62 | } 63 | 64 | .quiet { 65 | color: #888; 66 | } 67 | small { 68 | font-size: 80%; 69 | } 70 | 71 | .selected_card .item .title, 72 | .item .title:hover { 73 | color: #1badee; 74 | } 75 | 76 | .item.active { 77 | background-color: #f8f8f8; 78 | } 79 | 80 | .selected_card { 81 | background-color: #f8f8f8; 82 | } 83 | 84 | .selected_card:hover { 85 | background-color: #f8f8f8; 86 | } 87 | 88 | ::-webkit-scrollbar { 89 | width: 5px; 90 | height: 5px; 91 | } 92 | 93 | ::-webkit-scrollbar-thumb { 94 | background-color: #d1d1d1; 95 | } 96 | 97 | ::-webkit-scrollbar-track { 98 | background-color: #F7F7F7; 99 | } 100 | 101 | /*grids*/ 102 | .pure-g { 103 | letter-spacing: -.31em; 104 | text-rendering: optimizespeed; 105 | display: -webkit-flex; 106 | -webkit-flex-flow: row wrap; 107 | display: -ms-flexbox; 108 | -ms-flex-flow: row wrap; 109 | -ms-align-content: flex-start; 110 | -webkit-align-content: flex-start; 111 | align-content: flex-start; 112 | } 113 | 114 | .pure-g [class *="pure-u"] { 115 | font-family: "Open Sans", "Helvetica Neue", Arial, Helvetica, Verdana, sans-serif; 116 | font-weight: normal; 117 | letter-spacing: 0.01em; 118 | } 119 | 120 | .pure-u-1, .u-sm-1-3, .u-sm-2-3 { 121 | display: inline-block; 122 | zoom: 1; 123 | letter-spacing: normal; 124 | word-spacing: normal; 125 | vertical-align: top; 126 | text-rendering: auto; 127 | } 128 | 129 | .pure-u-1 { 130 | width: 100%; 131 | } 132 | 133 | @media screen and (min-width: 35.5em) { 134 | .u-sm-1-3 { 135 | width: 33.3333%; 136 | } 137 | 138 | .u-sm-2-3 { 139 | width: 66.5%; 140 | } 141 | } -------------------------------------------------------------------------------- /jsfiddle-samples/locator-map/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: LocatorMap - Woosmap Javascript API Locator Map Demo 3 | description: jsFiddle demo that display a more complete store locator map using Woosmap Javascript API. 4 | authors: 5 | - Woosmap DevTeam 6 | ... -------------------------------------------------------------------------------- /jsfiddle-samples/locator-map/demo.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 9 |
10 |
-------------------------------------------------------------------------------- /jsfiddle-samples/locator-map/demo.js: -------------------------------------------------------------------------------- 1 | var projectKey = '12345678'; 2 | var markersStyle = { 3 | rules: [ 4 | { 5 | type: 'drive', 6 | icon: {url: 'https://developers.woosmap.com/img/markers/marker_drive.png', scaledSize: {width: 46/2, height: 58/2}}, 7 | selectedIcon: {url: 'https://developers.woosmap.com/img/markers/marker_selected.png', scaledSize: {width: 46, height: 58}} 8 | } 9 | ], 10 | default: { 11 | icon: {url: 'https://developers.woosmap.com/img/markers/marker_default.png', scaledSize: {width: 46/2, height: 58/2}}, 12 | selectedIcon: {url: 'https://developers.woosmap.com/img/markers/marker_selected.png', scaledSize: {width: 46, height: 58}} 13 | } 14 | }; 15 | var tilesStyle = { 16 | color: '#383838', 17 | size: 11, 18 | minSize: 6, 19 | typeRules: [{ 20 | type: 'drive', 21 | color: '#82a859' 22 | }] 23 | }; 24 | 25 | 26 | /*----- Init and display a Map with a TiledLayer-----*/ 27 | function woosmap_main() { 28 | var self = this; 29 | var loader = new woosmap.MapsLoader(); 30 | var dataSource = new woosmap.DataSource(); 31 | loader.load(function () { 32 | var tableview = new woosmap.ui.TableView({ 33 | cell: '
' + 34 | '{{name}}
{{address.city}}
' + 35 | '
{{address.lines}} {{address.city}} {{address.zip}}
' 36 | }); 37 | 38 | var listings = woosmap.$('#listings'); 39 | listings.append(tableview.getContainer()); 40 | self.tableview = tableview; 41 | var map = new google.maps.Map(woosmap.$('#my-map')[0], { 42 | center: {lat: 46, lng: 3}, 43 | zoom: 5 44 | }); 45 | var mapView = new woosmap.TiledView(map, {style: markersStyle, tileStyle: tilesStyle}); 46 | mapView.bindTo('stores', tableview, 'stores', false); 47 | mapView.bindTo('selectedStore', tableview, 'selectedStore', false); 48 | 49 | dataSource.getAllStores(function (stores) { 50 | tableview.set('stores', stores.features); 51 | }); 52 | }); 53 | 54 | } 55 | 56 | document.addEventListener("DOMContentLoaded", function (event) { 57 | WoosmapLoader.load('1.2', projectKey, woosmap_main); 58 | }); -------------------------------------------------------------------------------- /jsfiddle-samples/map-infowindow/README.MD: -------------------------------------------------------------------------------- 1 | 2 | In order to view this demo on JSFiddle, open this URL: 3 | https://fiddle.jshell.net/gh/get/library/pure/woosmap/samples/tree/master/jsfiddle-samples/map-infowindow/ 4 | 5 | -------------------------------------------------------------------------------- /jsfiddle-samples/map-infowindow/demo.css: -------------------------------------------------------------------------------- 1 | #my-map { 2 | height: 400px; 3 | width: 100%; 4 | } 5 | 6 | .info-item { 7 | line-height: 2; 8 | display: block; 9 | overflow: hidden; 10 | white-space: nowrap; 11 | text-decoration: none; 12 | color: #555; 13 | } 14 | 15 | .info-item .title { 16 | display: block; 17 | color: #4d9da9; 18 | font-weight: 500; 19 | background: url('https://developers.woosmap.com/img/Punaise_WGS_V3.png') no-repeat top left; 20 | background-size: auto 58px; 21 | padding-left: 70px; 22 | } 23 | 24 | .quiet { 25 | color: #888; 26 | } 27 | 28 | small { 29 | font-size: 80%; 30 | } -------------------------------------------------------------------------------- /jsfiddle-samples/map-infowindow/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: MapInfowindow - Woosmap Javascript API MapInfowindow Demo 3 | description: jsFiddle demo that display a TiledView of a sample datasource using Woosmap Javascript API and allow to click location to open InfoWindow. 4 | authors: 5 | - Woosmap DevTeam 6 | ... -------------------------------------------------------------------------------- /jsfiddle-samples/map-infowindow/demo.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | -------------------------------------------------------------------------------- /jsfiddle-samples/map-infowindow/demo.js: -------------------------------------------------------------------------------- 1 | var projectKey = '12345678'; 2 | var markersStyle = { 3 | rules: [ 4 | { 5 | type: 'drive', 6 | icon: {url: 'https://images.woosmap.com/marker_drive.svg', scaledSize: {width: 36, height: 48}}, 7 | selectedIcon: {url: 'https://images.woosmap.com/marker_drive_selected.svg', scaledSize: {width: 46, height: 60}} 8 | } 9 | ], 10 | default: { 11 | icon: {url: 'https://images.woosmap.com/marker_default.svg', scaledSize: {width: 36, height: 48}}, 12 | selectedIcon: {url: 'https://images.woosmap.com/marker_selected.svg', scaledSize: {width: 46, height: 60}} 13 | } 14 | }; 15 | var tilesStyle = { 16 | color: '#383838', 17 | size: 11, 18 | minSize: 6, 19 | typeRules: [{ 20 | type: 'drive', 21 | color: '#82a859' 22 | }] 23 | }; 24 | 25 | /*----- Init and display a Map with a TiledLayer-----*/ 26 | function woosmap_main() { 27 | var loader = new woosmap.MapsLoader(); 28 | var dataSource = new woosmap.DataSource(); 29 | loader.load(function () { 30 | var map = new google.maps.Map(woosmap.$('#my-map')[0], { 31 | center: {lat: 46, lng: 3}, 32 | zoom: 5 33 | }); 34 | var template = '
' + 35 | '{{name}}
{{address.city}}
' + 36 | '
{{address.lines}} {{address.city}} {{address.zip}}
' + 37 | '
Tel : {{contact.phone}}
'; 38 | 39 | var renderer = new woosmap.TemplateRenderer(template); 40 | var win = new woosmap.LocatorWindow(map, renderer); 41 | 42 | var mapView = new woosmap.TiledView(map, {style: markersStyle, tileStyle: tilesStyle}); 43 | win.bindTo('selectedStore', mapView); 44 | }); 45 | 46 | } 47 | 48 | document.addEventListener("DOMContentLoaded", function (event) { 49 | WoosmapLoader.load('1.2', projectKey, woosmap_main); 50 | }); 51 | -------------------------------------------------------------------------------- /jsfiddle-samples/map-tiled-view/README.MD: -------------------------------------------------------------------------------- 1 | 2 | In order to view this demo on JSFiddle, open this URL: 3 | https://fiddle.jshell.net/gh/get/library/pure/woosmap/samples/tree/master/jsfiddle-samples/map-tiled-view/ 4 | 5 | -------------------------------------------------------------------------------- /jsfiddle-samples/map-tiled-view/demo.css: -------------------------------------------------------------------------------- 1 | #my-map { 2 | height: 400px; 3 | width: 100%; 4 | } 5 | 6 | #go-to-paris { 7 | padding: 8px; 8 | border-style: none; 9 | border-radius: 2px; 10 | box-shadow: rgba(0, 0, 0, 0.298039) 0 1px 4px -1px; 11 | background-color: rgb(255, 255, 255); 12 | cursor: pointer; 13 | } 14 | 15 | .btn-container { 16 | margin: 18px; 17 | z-index: 0; 18 | position: absolute; 19 | right: 0; 20 | top: 0; 21 | } 22 | -------------------------------------------------------------------------------- /jsfiddle-samples/map-tiled-view/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: BasicTiledView - Woosmap Javascript API TiledView Demo 3 | description: jsFiddle demo that display a TiledView of a sample datasource using Woosmap Javascript API. 4 | authors: 5 | - Woosmap DevTeam 6 | ... -------------------------------------------------------------------------------- /jsfiddle-samples/map-tiled-view/demo.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 |
6 | -------------------------------------------------------------------------------- /jsfiddle-samples/map-tiled-view/demo.js: -------------------------------------------------------------------------------- 1 | var projectKey = '12345678'; 2 | var markersStyle = { 3 | rules: [ 4 | { 5 | type: 'drive', 6 | icon: {url: 'https://developers.woosmap.com/img/markers/marker_drive.png', scaledSize: {width: 36, height: 48}}, 7 | selectedIcon: {url: 'https://developers.woosmap.com/img/markers/marker_selected.png', scaledSize: {width: 46, height: 60}} 8 | } 9 | ], 10 | default: { 11 | icon: {url: 'https://developers.woosmap.com/img/markers/marker_default.png', scaledSize: {width: 36, height: 48}}, 12 | selectedIcon: {url: 'https://developers.woosmap.com/img/markers/marker_selected.png', scaledSize: {width: 46, height: 60}} 13 | } 14 | }; 15 | var tilesStyle = { 16 | color: '#383838', 17 | size: 11, 18 | minSize: 6, 19 | typeRules: [{ 20 | type: 'drive', 21 | color: '#82a859' 22 | }] 23 | }; 24 | 25 | /*----- Handle store selection -----*/ 26 | function registerLocationClickEvent(mapView) { 27 | var selectedStoreObserver = new woosmap.utils.MVCObject(); 28 | selectedStoreObserver.selectedStore = null; 29 | selectedStoreObserver.selectedStore_changed = function () { 30 | var selectedStore = this.get('selectedStore'); 31 | alert(selectedStore.properties.name); 32 | }; 33 | selectedStoreObserver.bindTo('selectedStore', mapView); 34 | } 35 | 36 | /*----- Store by Location, with distance -----*/ 37 | function registerNearbyClickEvent(mapView, dataSource) { 38 | var MAX_STORE = 10; 39 | var MAX_DISTANCE_FROM_LOCATION = 150000; //150km 40 | var nearbyStoreSource = new woosmap.location.NearbyStoresSource(dataSource, MAX_STORE, MAX_DISTANCE_FROM_LOCATION); 41 | nearbyStoreSource.bindTo('location', mapView); 42 | nearbyStoreSource.bindTo('stores', mapView); 43 | 44 | woosmap.$('#go-to-paris').on('click', function () { 45 | mapView.set('location', { 46 | lat: 48.85, 47 | lng: 2.27 48 | }); 49 | }); 50 | } 51 | 52 | function registerDraggableMarker(mapView) { 53 | mapView.marker.setOptions({ 54 | draggable: true, 55 | icon: {url: 'https://developers.woosmap.com/img/markers/geolocated.png'} 56 | }); 57 | } 58 | /*----- Init and display a Map with a TiledLayer-----*/ 59 | function woosmap_main() { 60 | var loader = new woosmap.MapsLoader(); 61 | var dataSource = new woosmap.DataSource(); 62 | loader.load(function () { 63 | var map = new google.maps.Map(woosmap.$('#my-map')[0], { 64 | center: {lat: 46, lng: 3}, 65 | zoom: 5 66 | }); 67 | var mapView = new woosmap.TiledView(map, {style: markersStyle, tileStyle: tilesStyle}); 68 | registerNearbyClickEvent(mapView, dataSource); 69 | registerLocationClickEvent(mapView); 70 | registerDraggableMarker(mapView); 71 | }); 72 | 73 | } 74 | 75 | document.addEventListener("DOMContentLoaded", function (event) { 76 | WoosmapLoader.load('1.2', projectKey, woosmap_main); 77 | }); 78 | -------------------------------------------------------------------------------- /jsfiddle-samples/search-location/README.MD: -------------------------------------------------------------------------------- 1 | 2 | In order to view this demo on JSFiddle, open this URL: 3 | https://fiddle.jshell.net/gh/get/library/pure/woosmap/samples/tree/master/jsfiddle-samples/search-location/ 4 | 5 | -------------------------------------------------------------------------------- /jsfiddle-samples/search-location/demo.css: -------------------------------------------------------------------------------- 1 | #my-map { 2 | height: 500px; 3 | } 4 | 5 | .locator-container { 6 | font-size: 15px; 7 | line-height: 1.5em; 8 | color: #555; 9 | background: #FFF; 10 | } 11 | 12 | .sidebar { 13 | height: 500px; 14 | overflow: hidden; 15 | border: 1px solid #EEE; 16 | border-right: none; 17 | } 18 | 19 | .listings { 20 | height: 460px; 21 | padding-bottom: 36px; 22 | background-color: #FFF; 23 | color: #555; 24 | } 25 | 26 | .woosmap-tableview-container, .card_container { 27 | max-height: 100%; 28 | overflow-y: scroll; 29 | overflow-x: hidden; 30 | } 31 | 32 | .item { 33 | display: block; 34 | border-bottom: 1px solid #eee; 35 | padding: 10px; 36 | text-decoration: none; 37 | cursor: pointer; 38 | } 39 | 40 | .item .title { 41 | display: block; 42 | color: #4d9da9; 43 | font-weight: 500; 44 | } 45 | 46 | .item .title small { 47 | font-weight: 300; 48 | } 49 | 50 | .quiet { 51 | color: #888; 52 | } 53 | 54 | small { 55 | font-size: 80%; 56 | } 57 | 58 | .selected_card .item .title, 59 | .item .title:hover { 60 | color: #1badee; 61 | } 62 | 63 | .item.active { 64 | background-color: #f8f8f8; 65 | } 66 | 67 | .selected_card { 68 | background-color: #f8f8f8; 69 | } 70 | 71 | .selected_card:hover { 72 | background-color: #f8f8f8; 73 | } 74 | 75 | .search_container { 76 | margin: 5px; 77 | border: 1px solid #1badee; 78 | border-radius: 2px; 79 | box-sizing: border-box; 80 | -moz-box-sizing: border-box; 81 | height: 32px; 82 | outline: none; 83 | padding: 0 7px; 84 | width: 96%; 85 | vertical-align: top; 86 | position: relative; 87 | } 88 | 89 | .search_input { 90 | border: none; 91 | padding: 0; 92 | height: 1.25em; 93 | width: 100%; 94 | z-index: 6; 95 | outline: none; 96 | background: #FFF; 97 | margin-top: 7px; 98 | float: left; 99 | font-size: 1em; 100 | color: #555; 101 | } 102 | 103 | .search_clear { 104 | float: right; 105 | background: white url('https://developers.woosmap.com/img/close.png') no-repeat left top; 106 | position: absolute; 107 | right: 5px; 108 | top: 8px; 109 | padding: 7px; 110 | font-size: 14px; 111 | cursor: pointer; 112 | display: none; 113 | } 114 | 115 | .search_clear:hover { 116 | background: white url('https://developers.woosmap.com/img/close-hover.png') no-repeat left top; 117 | } 118 | 119 | .woosmap-tableview-highlighted-cell { 120 | background-color: #f8f8f8; 121 | } 122 | 123 | ::-webkit-scrollbar { 124 | width: 5px; 125 | height: 5px; 126 | } 127 | 128 | ::-webkit-scrollbar-thumb { 129 | background-color: #d1d1d1; 130 | } 131 | 132 | ::-webkit-scrollbar-track { 133 | background-color: #F7F7F7; 134 | } 135 | 136 | /*grids*/ 137 | .pure-g { 138 | letter-spacing: -.31em; 139 | text-rendering: optimizespeed; 140 | display: -webkit-flex; 141 | -webkit-flex-flow: row wrap; 142 | display: -ms-flexbox; 143 | -ms-flex-flow: row wrap; 144 | -ms-align-content: flex-start; 145 | -webkit-align-content: flex-start; 146 | align-content: flex-start; 147 | } 148 | 149 | .pure-g [class *="pure-u"] { 150 | font-family: "Open Sans", "Helvetica Neue", Arial, Helvetica, Verdana, sans-serif; 151 | font-weight: normal; 152 | letter-spacing: 0.01em; 153 | } 154 | 155 | .pure-u-1, .u-sm-1-3, .u-sm-2-3 { 156 | display: inline-block; 157 | zoom: 1; 158 | letter-spacing: normal; 159 | word-spacing: normal; 160 | vertical-align: top; 161 | text-rendering: auto; 162 | } 163 | 164 | .pure-u-1 { 165 | width: 100%; 166 | } 167 | 168 | @media screen and (min-width: 35.5em) { 169 | .u-sm-1-3 { 170 | width: 33.3333%; 171 | } 172 | 173 | .u-sm-2-3 { 174 | width: 66.5%; 175 | } 176 | } -------------------------------------------------------------------------------- /jsfiddle-samples/search-location/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: SearchLocation - Woosmap Javascript API Search Location Demo 3 | description: jsFiddle demo that allow the use of Google maps basic geocoding using Woosmap Javascript API. 4 | authors: 5 | - Woosmap DevTeam 6 | ... -------------------------------------------------------------------------------- /jsfiddle-samples/search-location/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 8 |
9 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /jsfiddle-samples/search-location/demo.js: -------------------------------------------------------------------------------- 1 | var projectKey = '12345678'; 2 | var markersStyle = { 3 | rules: [ 4 | { 5 | type: 'drive', 6 | icon: {url: 'https://images.woosmap.com/marker_drive.svg', scaledSize: {width: 36, height: 48}}, 7 | selectedIcon: {url: 'https://images.woosmap.com/marker_drive_selected.svg', scaledSize: {width: 46, height: 60}}, 8 | numberedIcon: {url: 'https://images.woosmap.com/marker_drive_selected.svg', scaledSize: {width: 46, height: 60}} 9 | } 10 | ], 11 | default: { 12 | icon: {url: 'https://images.woosmap.com/marker_default.svg', scaledSize: {width: 36, height: 48}}, 13 | selectedIcon: {url: 'https://images.woosmap.com/marker_selected.svg', scaledSize: {width: 46, height: 60}} 14 | } 15 | }; 16 | var tilesStyle = { 17 | color: '#383838', 18 | size: 11, 19 | minSize: 6, 20 | typeRules: [{ 21 | type: 'drive', 22 | color: '#82a859' 23 | }] 24 | }; 25 | 26 | function registerDraggableMarker(mapView) { 27 | mapView.marker.setOptions({ 28 | draggable: true, 29 | icon: {url: 'https://developers.woosmap.com/img/markers/geolocated.png'} 30 | }); 31 | } 32 | 33 | /*----- Init and display a Map with a TiledLayer-----*/ 34 | function woosmap_main() { 35 | var self = this; 36 | var loader = new woosmap.MapsLoader(); 37 | var dataSource = new woosmap.DataSource(); 38 | loader.load(function () { 39 | var tableview = new woosmap.ui.TableView({ 40 | cell_store: '
' + 41 | '{{name}}
{{address.city}}
' + 42 | '
{{address.lines}} {{address.city}} {{address.zip}}
', 43 | cell_place: '
{{description}}
' 44 | }); 45 | var geocoder = new woosmap.location.GeocoderSearchSource(); 46 | var searchview = new woosmap.ui.SearchView(woosmap.$('#search_template').text()); 47 | var nearbyStoresSource = new woosmap.location.NearbyStoresSource(dataSource, 5); 48 | 49 | nearbyStoresSource.bindTo('location', geocoder, 'location', false); 50 | tableview.bindTo('stores', nearbyStoresSource); 51 | geocoder.bindTo('query', searchview, 'query', false); 52 | 53 | var listings = woosmap.$('#listings'); 54 | var sidebar = woosmap.$('.sidebar'); 55 | 56 | sidebar.prepend(searchview.getContainer()); 57 | listings.append(tableview.getContainer()); 58 | 59 | self.tableview = tableview; 60 | var defaultStores = null; 61 | var map = new google.maps.Map(woosmap.$('#my-map')[0], { 62 | center: {lat: 46, lng: 3}, 63 | zoom: 5 64 | }); 65 | 66 | var mapView = new woosmap.TiledView(map, {style: markersStyle, tileStyle: tilesStyle}); 67 | mapView.bindTo('stores', tableview, 'stores', false); 68 | mapView.bindTo('selectedStore', tableview, 'selectedStore', false); 69 | mapView.bindTo('location', geocoder); 70 | mapView.delegate = { 71 | 'didLocationMarkerDragEnd': function () { 72 | searchview.$searchInput.val(''); 73 | } 74 | }; 75 | 76 | registerDraggableMarker(mapView); 77 | 78 | searchview.delegate = { 79 | didClearSearch: function () { 80 | tableview.set('stores', defaultStores); 81 | mapView.set('selectedStore', null); 82 | mapView.set('location', {}) 83 | } 84 | }; 85 | 86 | }); 87 | 88 | } 89 | 90 | document.addEventListener("DOMContentLoaded", function (event) { 91 | WoosmapLoader.load('latest', projectKey, woosmap_main); 92 | }); -------------------------------------------------------------------------------- /jsfiddle-samples/search-places/README.MD: -------------------------------------------------------------------------------- 1 | 2 | In order to view this demo on JSFiddle, open this URL: 3 | https://fiddle.jshell.net/gh/get/library/pure/woosmap/samples/tree/master/jsfiddle-samples/search-places/ 4 | 5 | -------------------------------------------------------------------------------- /jsfiddle-samples/search-places/demo.css: -------------------------------------------------------------------------------- 1 | #my-map { 2 | height: 500px; 3 | } 4 | 5 | .locator-container { 6 | font-size: 15px; 7 | line-height: 1.5em; 8 | color: #555; 9 | background: #FFF; 10 | } 11 | 12 | .sidebar { 13 | height: 500px; 14 | overflow: hidden; 15 | border: 1px solid #EEE; 16 | border-right: none; 17 | } 18 | 19 | .listings { 20 | height: 460px; 21 | padding-bottom: 36px; 22 | background-color: #FFF; 23 | color: #555; 24 | } 25 | 26 | .woosmap-tableview-container, .card_container { 27 | max-height: 100%; 28 | overflow-y: scroll; 29 | overflow-x: hidden; 30 | } 31 | 32 | .item { 33 | display: block; 34 | border-bottom: 1px solid #eee; 35 | padding: 10px; 36 | text-decoration: none; 37 | cursor: pointer; 38 | } 39 | 40 | .item .title { 41 | display: block; 42 | color: #4d9da9; 43 | font-weight: 500; 44 | } 45 | 46 | .item .title small { 47 | font-weight: 300; 48 | } 49 | 50 | .quiet { 51 | color: #888; 52 | } 53 | 54 | small { 55 | font-size: 80%; 56 | } 57 | 58 | .selected_card .item .title, 59 | .item .title:hover { 60 | color: #1badee; 61 | } 62 | 63 | .item.active { 64 | background-color: #f8f8f8; 65 | } 66 | 67 | .selected_card { 68 | background-color: #f8f8f8; 69 | } 70 | 71 | .selected_card:hover { 72 | background-color: #f8f8f8; 73 | } 74 | 75 | .search_container { 76 | margin: 5px; 77 | border: 1px solid #1badee; 78 | border-radius: 2px; 79 | box-sizing: border-box; 80 | -moz-box-sizing: border-box; 81 | height: 32px; 82 | outline: none; 83 | padding: 0 7px; 84 | width: 96%; 85 | vertical-align: top; 86 | position: relative; 87 | } 88 | 89 | .search_input { 90 | border: none; 91 | padding: 0; 92 | height: 1.25em; 93 | width: 100%; 94 | z-index: 6; 95 | outline: none; 96 | background: #FFF; 97 | margin-top: 7px; 98 | float: left; 99 | font-size: 1em; 100 | color: #555; 101 | } 102 | 103 | .search_clear { 104 | float: right; 105 | background: white url('https://developers.woosmap.com/img/close.png') no-repeat left top; 106 | position: absolute; 107 | right: 5px; 108 | top: 8px; 109 | padding: 7px; 110 | font-size: 14px; 111 | cursor: pointer; 112 | display: none; 113 | } 114 | 115 | .search_clear:hover { 116 | background: white url('https://developers.woosmap.com/img/close-hover.png') no-repeat left top; 117 | } 118 | 119 | .woosmap-tableview-highlighted-cell { 120 | background-color: #f8f8f8; 121 | } 122 | 123 | ::-webkit-scrollbar { 124 | width: 5px; 125 | height: 5px; 126 | } 127 | 128 | ::-webkit-scrollbar-thumb { 129 | background-color: #d1d1d1; 130 | } 131 | 132 | ::-webkit-scrollbar-track { 133 | background-color: #F7F7F7; 134 | } 135 | 136 | /*grids*/ 137 | .pure-g { 138 | letter-spacing: -.31em; 139 | text-rendering: optimizespeed; 140 | display: -webkit-flex; 141 | -webkit-flex-flow: row wrap; 142 | display: -ms-flexbox; 143 | -ms-flex-flow: row wrap; 144 | -ms-align-content: flex-start; 145 | -webkit-align-content: flex-start; 146 | align-content: flex-start; 147 | } 148 | 149 | .pure-g [class *="pure-u"] { 150 | font-family: "Open Sans", "Helvetica Neue", Arial, Helvetica, Verdana, sans-serif; 151 | font-weight: normal; 152 | letter-spacing: 0.01em; 153 | } 154 | 155 | .pure-u-1, .u-sm-1-3, .u-sm-2-3 { 156 | display: inline-block; 157 | zoom: 1; 158 | letter-spacing: normal; 159 | word-spacing: normal; 160 | vertical-align: top; 161 | text-rendering: auto; 162 | } 163 | 164 | .pure-u-1 { 165 | width: 100%; 166 | } 167 | 168 | @media screen and (min-width: 35.5em) { 169 | .u-sm-1-3 { 170 | width: 33.3333%; 171 | } 172 | 173 | .u-sm-2-3 { 174 | width: 66.5%; 175 | } 176 | } -------------------------------------------------------------------------------- /jsfiddle-samples/search-places/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: SearchPlaces - Woosmap Javascript API Search Places Demo 3 | description: jsFiddle demo that allow the use of Google maps Autocomplete using Woosmap Javascript API. 4 | authors: 5 | - Woosmap DevTeam 6 | ... -------------------------------------------------------------------------------- /jsfiddle-samples/search-places/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 8 |
9 | 12 |
13 |
-------------------------------------------------------------------------------- /jsfiddle-samples/search-places/demo.js: -------------------------------------------------------------------------------- 1 | var projectKey = '12345678'; 2 | var markersStyle = { 3 | rules: [{ 4 | type: 'drive', 5 | icon: { 6 | url: 'https://images.woosmap.com/marker_drive.svg', 7 | scaledSize: { 8 | width: 36, 9 | height: 48 10 | } 11 | }, 12 | selectedIcon: { 13 | url: 'https://images.woosmap.com/marker_drive_selected.svg', 14 | scaledSize: { 15 | width: 46, 16 | height: 60 17 | } 18 | } 19 | }], 20 | default: { 21 | icon: { 22 | url: 'https://images.woosmap.com/marker_default.svg', 23 | scaledSize: { 24 | width: 36, 25 | height: 48 26 | } 27 | }, 28 | selectedIcon: { 29 | url: 'https://images.woosmap.com/marker_selected.svg', 30 | scaledSize: { 31 | width: 46, 32 | height: 60 33 | } 34 | } 35 | } 36 | }; 37 | var tilesStyle = { 38 | color: '#383838', 39 | size: 11, 40 | minSize: 6, 41 | typeRules: [{ 42 | type: 'drive', 43 | color: '#82a859' 44 | }] 45 | }; 46 | 47 | function registerDraggableMarker(mapView) { 48 | mapView.marker.setOptions({ 49 | draggable: true, 50 | icon: {url: 'https://developers.woosmap.com/img/markers/geolocated.png'} 51 | }); 52 | } 53 | 54 | /*----- Init and display a Map with a TiledLayer-----*/ 55 | function woosmap_main() { 56 | var self = this; 57 | var loader = new woosmap.MapsLoader("", ['places']); 58 | var dataSource = new woosmap.DataSource(); 59 | loader.load(function () { 60 | 61 | var searchBounds = new google.maps.LatLngBounds( 62 | new google.maps.LatLng(-5, 42), 63 | new google.maps.LatLng(9, 52) 64 | ); 65 | var googlePlaceAutocompleteOptions = { 66 | bounds: searchBounds, 67 | types: ['geocode'], 68 | componentRestrictions: {country: 'fr'} 69 | }; 70 | var tableview = new woosmap.ui.TableView({ 71 | cell_store: '
' + 72 | '{{name}}
{{address.city}}
' + 73 | '
{{address.lines}} {{address.city}} {{address.zip}}
', 74 | cell_place: '
{{description}}
' 75 | }); 76 | var searchview = new woosmap.ui.SearchView(woosmap.$('#search_template').text()); 77 | var nearbyStoresSource = new woosmap.location.NearbyStoresSource(dataSource, 5); 78 | var placesSearchSource = new woosmap.location.PlacesSearchSource(googlePlaceAutocompleteOptions); 79 | 80 | tableview.bindTo('stores', nearbyStoresSource); 81 | tableview.bindTo('predictions', placesSearchSource); 82 | 83 | placesSearchSource.bindTo('autocomplete_query', searchview, false); 84 | 85 | var listings = woosmap.$('#listings'); 86 | var sidebar = woosmap.$('.sidebar'); 87 | 88 | sidebar.prepend(searchview.getContainer()); 89 | listings.append(tableview.getContainer()); 90 | 91 | self.tableview = tableview; 92 | 93 | var map = new google.maps.Map(woosmap.$('#my-map')[0], { 94 | center: {lat: 46, lng: 3}, 95 | zoom: 5 96 | }); 97 | 98 | var mapView = new woosmap.TiledView(map, {style: markersStyle, tileStyle: tilesStyle}); 99 | tableview.bindTo('location', mapView); 100 | nearbyStoresSource.bindTo('location', mapView); 101 | mapView.bindTo('stores', tableview, 'stores', false); 102 | mapView.bindTo('selectedStore', tableview, 'selectedStore', false); 103 | 104 | mapView.marker.setOptions({ 105 | draggable: true 106 | }); 107 | searchview.delegate = { 108 | didClearSearch: function () { 109 | tableview.set('stores', []); 110 | tableview.set('predictions', []); 111 | mapView.set('selectedStore', null); 112 | mapView.set('location', {}) 113 | } 114 | }; 115 | 116 | registerDraggableMarker(mapView); 117 | }); 118 | 119 | } 120 | 121 | document.addEventListener("DOMContentLoaded", function (event) { 122 | WoosmapLoader.load('1.2', projectKey, woosmap_main); 123 | }); -------------------------------------------------------------------------------- /jsfiddle-samples/search-query/README.MD: -------------------------------------------------------------------------------- 1 | 2 | In order to view this demo on JSFiddle, open this URL: 3 | https://fiddle.jshell.net/gh/get/library/pure/woosmap/samples/tree/master/jsfiddle-samples/search-query/ 4 | 5 | -------------------------------------------------------------------------------- /jsfiddle-samples/search-query/demo.css: -------------------------------------------------------------------------------- 1 | #my-map { 2 | height: 600px; 3 | } 4 | 5 | .locator-container { 6 | font-size: 15px; 7 | line-height: 1.5em; 8 | color: #555; 9 | background: #FFF; 10 | } 11 | 12 | .sidebar { 13 | height: 600px; 14 | overflow: hidden; 15 | border: 1px solid #EEE; 16 | border-right: none; 17 | } 18 | 19 | .listings { 20 | 21 | padding-bottom: 36px; 22 | background-color: #FFF; 23 | color: #555; 24 | } 25 | 26 | .woosmap-tableview-container, .card_container { 27 | max-height: 100%; 28 | overflow-y: scroll; 29 | overflow-x: hidden; 30 | } 31 | 32 | .item { 33 | display: block; 34 | border-bottom: 1px solid #eee; 35 | padding: 10px; 36 | text-decoration: none; 37 | cursor: pointer; 38 | } 39 | 40 | .item .title { 41 | display: block; 42 | color: #4d9da9; 43 | font-weight: 500; 44 | } 45 | 46 | .item .title small { 47 | font-weight: 300; 48 | } 49 | 50 | .quiet { 51 | color: #888; 52 | } 53 | 54 | small { 55 | font-size: 80%; 56 | } 57 | 58 | .selected_card .item .title, 59 | .item .title:hover { 60 | color: #1badee; 61 | } 62 | 63 | .item.active { 64 | background-color: #f8f8f8; 65 | } 66 | 67 | .selected_card { 68 | background-color: #f8f8f8; 69 | } 70 | 71 | .selected_card:hover { 72 | background-color: #f8f8f8; 73 | } 74 | 75 | .search_container { 76 | margin: 5px; 77 | border: 1px solid #1badee; 78 | border-radius: 2px; 79 | box-sizing: border-box; 80 | -moz-box-sizing: border-box; 81 | height: 32px; 82 | outline: none; 83 | padding: 0 7px; 84 | width: 96%; 85 | vertical-align: top; 86 | position: relative; 87 | } 88 | 89 | .search_input { 90 | border: none; 91 | padding: 0; 92 | height: 1.25em; 93 | width: 100%; 94 | z-index: 6; 95 | outline: none; 96 | background: #FFF; 97 | margin-top: 7px; 98 | float: left; 99 | font-size: 1em; 100 | color: #555; 101 | } 102 | 103 | .search_clear { 104 | float: right; 105 | background: white url('https://developers.woosmap.com/img/close.png') no-repeat left top; 106 | position: absolute; 107 | right: 5px; 108 | top: 8px; 109 | padding: 7px; 110 | font-size: 14px; 111 | cursor: pointer; 112 | display: none; 113 | } 114 | 115 | .search_clear:hover { 116 | background: white url('https://developers.woosmap.com/img/close-hover.png') no-repeat left top; 117 | } 118 | 119 | .woosmap-tableview-highlighted-cell { 120 | background-color: #f8f8f8; 121 | } 122 | 123 | ::-webkit-scrollbar { 124 | width: 5px; 125 | height: 5px; 126 | } 127 | 128 | ::-webkit-scrollbar-thumb { 129 | background-color: #d1d1d1; 130 | } 131 | 132 | ::-webkit-scrollbar-track { 133 | background-color: #F7F7F7; 134 | } 135 | 136 | /* attribute search page */ 137 | 138 | .attributes-search-container { 139 | border-bottom: solid 1px #eee; 140 | 141 | } 142 | 143 | .attribute-filter-container { 144 | margin: 5px; 145 | border: 1px solid rgba(27, 173, 238, 0.29); 146 | } 147 | 148 | .attribute-filter-container:hover { 149 | margin: 5px; 150 | border: 1px solid #1badee; 151 | } 152 | 153 | .attribute-filter-title { 154 | cursor: pointer; 155 | text-align: center; 156 | } 157 | 158 | .attribute-filter-title.active { 159 | border-bottom: solid 1px #eee; 160 | } 161 | 162 | .attribute-filter-body { 163 | margin-left: 5px; 164 | } 165 | 166 | .checkbox-input-attribute { 167 | margin-right: 5px; 168 | } 169 | 170 | .search-attributes-listings { 171 | height: 320px; 172 | padding-bottom: 36px; 173 | } 174 | 175 | .woosmap-tableview-highlighted-cell .title { 176 | color: #1badee; 177 | } 178 | 179 | .woosmap-tableview-selected-cell { 180 | color: #1badee; 181 | background-color: rgba(235, 235, 235, 0.29); 182 | } 183 | 184 | /*grids*/ 185 | .pure-g { 186 | letter-spacing: -.31em; 187 | text-rendering: optimizespeed; 188 | display: -webkit-flex; 189 | -webkit-flex-flow: row wrap; 190 | display: -ms-flexbox; 191 | -ms-flex-flow: row wrap; 192 | -ms-align-content: flex-start; 193 | -webkit-align-content: flex-start; 194 | align-content: flex-start; 195 | } 196 | 197 | .pure-g [class *="pure-u"] { 198 | font-family: "Open Sans", "Helvetica Neue", Arial, Helvetica, Verdana, sans-serif; 199 | font-weight: normal; 200 | letter-spacing: 0.01em; 201 | } 202 | 203 | .pure-u-1, .u-sm-1-3, .u-sm-2-3 { 204 | display: inline-block; 205 | zoom: 1; 206 | letter-spacing: normal; 207 | word-spacing: normal; 208 | vertical-align: top; 209 | text-rendering: auto; 210 | } 211 | 212 | .pure-u-1 { 213 | width: 100%; 214 | } 215 | 216 | @media screen and (min-width: 35.5em) { 217 | .u-sm-1-3 { 218 | width: 33.3333%; 219 | } 220 | 221 | .u-sm-2-3 { 222 | width: 66.5%; 223 | } 224 | } -------------------------------------------------------------------------------- /jsfiddle-samples/search-query/demo.details: -------------------------------------------------------------------------------- 1 | --- 2 | name: SearchAttributes - Woosmap Javascript API Search Attributes Demo 3 | description: jsFiddle demo that allow you to search the data attributes using Woosmap Javascript API. 4 | authors: 5 | - Woosmap DevTeam 6 | ... -------------------------------------------------------------------------------- /jsfiddle-samples/search-query/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 26 | 41 | 53 | 54 |
55 | 58 |
59 |
60 | 61 | -------------------------------------------------------------------------------- /jsfiddle-samples/search-query/demo.js: -------------------------------------------------------------------------------- 1 | var projectKey = '12345678'; 2 | var markersStyle = { 3 | rules: [ 4 | { 5 | type: 'drive', 6 | icon: {url: 'https://images.woosmap.com/marker_drive.svg', scaledSize: {width: 36, height: 48}}, 7 | selectedIcon: {url: 'https://images.woosmap.com/marker_drive_selected.svg', scaledSize: {width: 46, height: 60}} 8 | } 9 | ], 10 | default: { 11 | icon: {url: 'https://dimages.woosmap.com/marker_default.svg', scaledSize: {width: 36, height: 48}}, 12 | selectedIcon: {url: 'https://images.woosmap.com/marker_selected.svg', scaledSize: {width: 46, height: 60}} 13 | } 14 | }; 15 | var tilesStyle = { 16 | color: '#383838', 17 | size: 11, 18 | minSize: 6, 19 | typeRules: [{ 20 | type: 'drive', 21 | color: '#82a859' 22 | }] 23 | }; 24 | 25 | function registerDraggableMarker(mapView) { 26 | mapView.marker.setOptions({ 27 | draggable: true, 28 | icon: { 29 | url: 'https://developers.woosmap.com/img/markers/geolocated.png' 30 | } 31 | }); 32 | } 33 | 34 | /*----- Init and display a Map with a TiledLayer-----*/ 35 | function woosmap_main() { 36 | var self = this; 37 | var loader = new woosmap.MapsLoader("", ['places']); 38 | var dataSource = new woosmap.DataSource(); 39 | loader.load(function () { 40 | 41 | var map = new google.maps.Map(woosmap.$('#my-map')[0], { 42 | center: { 43 | lat: 46, 44 | lng: 3 45 | }, 46 | zoom: 5 47 | }); 48 | 49 | var mapView = new woosmap.TiledView(map, { 50 | style: markersStyle, 51 | tileStyle: tilesStyle 52 | }); 53 | 54 | var searchview = new woosmap.ui.SearchView(woosmap.$('#search_template').text()); 55 | 56 | var initialSearchTextOptions = { 57 | name: woosmap.search.SearchQuery.OR, 58 | city: woosmap.search.SearchQuery.OR 59 | }; 60 | var searchQuery = new woosmap.search.SearchQuery(initialSearchTextOptions); 61 | 62 | var searchTextOptionsRenderer = new woosmap.TemplateRenderer(woosmap.$("#text-search-options-template").html()); 63 | var tagsRenderer = new woosmap.TemplateRenderer(woosmap.$("#tags-selector-template").html()); 64 | var typesRenderer = new woosmap.TemplateRenderer(woosmap.$("#types-selector-template").html()); 65 | var attributeSearchContainer = woosmap.$('
'); 66 | attributeSearchContainer.append(searchview.getContainer()); 67 | attributeSearchContainer.append(searchTextOptionsRenderer.render()); 68 | attributeSearchContainer.append(tagsRenderer.render()); 69 | attributeSearchContainer.append(typesRenderer.render()); 70 | var sidebar = woosmap.$('.sidebar'); 71 | sidebar.prepend(attributeSearchContainer); 72 | 73 | function typeChanged() { 74 | searchQuery.types = []; 75 | woosmap.$.each(woosmap.$('.woosmap-available-type:checked'), function (index, object) { 76 | searchQuery.addTypeFilter(woosmap.$(object).val(), woosmap.search.SearchQuery.AND); 77 | }); 78 | mapView.setSearchQuery(searchQuery); 79 | } 80 | 81 | function tagChanged() { 82 | searchQuery.tags = []; 83 | woosmap.$.each(woosmap.$('.woosmap-available-tag:checked'), function (index, object) { 84 | searchQuery.addTagFilter(woosmap.$(object).val(), woosmap.search.SearchQuery.AND); 85 | }); 86 | mapView.setSearchQuery(searchQuery); 87 | } 88 | 89 | woosmap.$('.woosmap-available-tag').click(function () { 90 | tagChanged(); 91 | }); 92 | 93 | woosmap.$('.woosmap-available-type').click(function () { 94 | typeChanged(); 95 | }); 96 | 97 | woosmap.$('.woosmap-text-search-param').click(function () { 98 | if (this.checked) { 99 | searchQuery.addQueryOption(woosmap.$(this).val(), woosmap.search.SearchQuery.OR) 100 | } else { 101 | searchQuery.removeQueryOption(woosmap.$(this).val()) 102 | } 103 | mapView.setSearchQuery(searchQuery); 104 | }); 105 | 106 | woosmap.$('.search_input').on("change paste keyup", function () { 107 | searchQuery.setQuery(woosmap.$(this).val()); 108 | mapView.setSearchQuery(searchQuery); 109 | }); 110 | 111 | searchview.delegate = { 112 | didClearSearch: function () { 113 | searchQuery.setQuery(''); 114 | mapView.setSearchQuery(searchQuery); 115 | } 116 | }; 117 | }); 118 | 119 | } 120 | 121 | document.addEventListener("DOMContentLoaded", function (event) { 122 | WoosmapLoader.load('1.2', projectKey, woosmap_main); 123 | }); 124 | 125 | -------------------------------------------------------------------------------- /python-samples/batchgeocoding/README.md: -------------------------------------------------------------------------------- 1 | **[UPDATE 31/01/2020]** 2 | The script has moved to a dedicated repository, [woosmap/geopy-googlemaps-batchgeocoder](https://github.com/woosmap/geopy-googlemaps-batchgeocoder), and updated to be compatible with Python3. 3 | --- 4 | 5 | # Python Script to batch geocode your csv address file using geopy and GoogleV3 Geocoding service 6 | 7 | **input** : csv file with addresses you need to geocode 8 | 9 | **output** : same csv with appended following fields 10 | 11 | - Latitude 12 | - Longitude 13 | - Location_Type 14 | - Formatted_Address 15 | - Error (if needded, for failed geocoded addresses) 16 | 17 | sample usage: 18 | 19 | python google_batch_geocoder.py 20 | 21 | 22 | **Mandatory parameters you have to set inside the python file** 23 | 24 | - ADDRESS_COLUMNS_NAME = ["name", "addressline1", "town"] 25 | *used to set a google geocoding query by merging this value into one string with comma separated. it depends on your CSV Input File* 26 | 27 | - NEW_COLUMNS_NAME = ["Lat", "Long", "Error", "formatted_address", "location_type"] 28 | *appended columns name to processed data csv* 29 | 30 | - DELIMITER = ";" 31 | *delimiter for input csv file* 32 | 33 | - INPUT_CSV_FILE = "./hairdresser_sample_addresses_sample.csv" 34 | *path and name for output csv file* 35 | 36 | - OUTPUT_CSV_FILE = "./processed.csv" 37 | *path and name for output csv file* 38 | 39 | **optional parameters** 40 | 41 | - COMPONENTS_RESTRICTIONS_COLUMNS_NAME = {"country": "IsoCode"} 42 | *used to define component restrictions for google geocoding* 43 | *see [Google componentRestrictions doc](https://developers.google.com/maps/documentation/javascript/reference?hl=FR#GeocoderComponentRestrictions) for details* 44 | 45 | - GOOGLE_SECRET_KEY = "1Asfgsdf5vR12XE1A6sfRd7=" 46 | *google secret key that allow you to geocode for Google API Premium accounts* 47 | 48 | - GOOGLE_CLIENT_ID = "gme-webgeoservicessa1" 49 | *google client ID, used to track and analyse your requests for Google API Premium accounts* 50 | 51 | 52 | 53 | **useful links** 54 | 55 | - [geopy](https://github.com/geopy/geopy) : Geocoding library for Python. 56 | - [Google Maps Geocoding API](https://developers.google.com/maps/documentation/geocoding/start) 57 | - [Google Maps Geocoding API Usage Limits](https://developers.google.com/maps/documentation/geocoding/usage-limits) 58 | -------------------------------------------------------------------------------- /python-samples/batchgeocoding/google_batch_geocoder.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | import time 4 | from _csv import QUOTE_MINIMAL 5 | from csv import Dialect 6 | from geopy.geocoders import GoogleV3 7 | from geopy.exc import ( 8 | GeocoderQueryError, 9 | GeocoderQuotaExceeded, 10 | ConfigurationError, 11 | GeocoderParseError, 12 | GeocoderTimedOut 13 | ) 14 | 15 | # used to set a google geocoding query by merging this value into one string with comma separated 16 | ADDRESS_COLUMNS_NAME = ["name", "addressline1", "town"] 17 | 18 | # used to define component restrictions for google geocoding 19 | COMPONENT_RESTRICTIONS_COLUMNS_NAME = {"country": "IsoCode"} 20 | 21 | # appended columns name to processed data csv 22 | NEW_COLUMNS_NAME = ["Lat", "Long", "Error", "formatted_address", "location_type"] 23 | 24 | # delimiter for input csv file 25 | DELIMITER = ";" 26 | 27 | # Automatically retry X times when GeocoderErrors occur (sometimes the API Service return intermittent failures). 28 | RETRY_COUNTER_CONST = 5 29 | 30 | dir = os.path.dirname(__file__) 31 | 32 | # path and name for output csv file 33 | INPUT_CSV_FILE = os.path.join(dir, "hairdresser_sample_addresses.csv") 34 | 35 | # path and name for output csv file 36 | OUTPUT_CSV_FILE = os.path.join(dir, "processed.csv") 37 | 38 | # google keys - see https://blog.woosmap.com for more details 39 | GOOGLE_SECRET_KEY = "" # important !! this key must stay private. TODO : pass this variable as a parameter to script 40 | GOOGLE_CLIENT_ID = "" # Only for Premium users so if used, you must also provide secret_key 41 | GOOGLE_API_KEY = "" # it will become a mandatory parameter soon 42 | 43 | 44 | # dialect to manage different format of CSV 45 | class CustomDialect(Dialect): 46 | delimiter = DELIMITER 47 | quotechar = '"' 48 | doublequote = True 49 | skipinitialspace = False 50 | lineterminator = '\n' 51 | quoting = QUOTE_MINIMAL 52 | 53 | 54 | csv.register_dialect('ga', CustomDialect) 55 | 56 | 57 | def process_addresses_from_csv(): 58 | geo_locator = GoogleV3(api_key=GOOGLE_API_KEY, 59 | client_id=GOOGLE_CLIENT_ID, 60 | secret_key=GOOGLE_SECRET_KEY) 61 | 62 | with open(INPUT_CSV_FILE, 'r') as csvinput: 63 | with open(OUTPUT_CSV_FILE, 'w') as csvoutput: 64 | 65 | # new csv based on same dialect as input csv 66 | writer = csv.writer(csvoutput, dialect="ga") 67 | 68 | reader = csv.DictReader(csvinput, dialect="ga") 69 | 70 | # 2-dimensional data variable used to write the new CSV 71 | processed_data = [] 72 | 73 | # append new columns, to receive geocoded information, to the header of the new CSV 74 | header = list(reader.fieldnames) 75 | for column_name in NEW_COLUMNS_NAME: 76 | header.append(column_name.strip()) 77 | processed_data.append(header) 78 | 79 | # iterate through each row of input CSV 80 | for record in reader: 81 | # build a line address based on the merge of multiple field values to pass to Google Geocoder 82 | line_address = ','.join( 83 | str(val) for val in (record[column_name] for column_name in ADDRESS_COLUMNS_NAME)) 84 | 85 | # if you want to use componentRestrictions feature, 86 | # build a matching dict {'googleComponentRestrictionField' : 'yourCSVFieldValue'} 87 | # to pass to Google Geocoder 88 | component_restrictions = {} 89 | if COMPONENT_RESTRICTIONS_COLUMNS_NAME: 90 | for key, value in COMPONENT_RESTRICTIONS_COLUMNS_NAME.items(): 91 | component_restrictions[key] = record[value] 92 | 93 | # geocode the built line_address and passing optional componentRestrictions 94 | location = geocode_address(geo_locator, line_address, component_restrictions) 95 | 96 | # build a new temp_row for each csv entry to append to process_data Array 97 | # first, append existing fieldnames value to this temp_row 98 | temp_row = [record[column_name] for column_name in reader.fieldnames] 99 | 100 | # then, append geocoded field value to this temp_row 101 | for column_name in NEW_COLUMNS_NAME: 102 | try: 103 | if isinstance(location[column_name], str): 104 | temp_row.append(location[column_name].encode('utf-8')) 105 | else: 106 | temp_row.append(location[column_name]) 107 | except BaseException as error: 108 | print(error) 109 | temp_row.append('') 110 | 111 | # to manage more precisely errors, you could use csvwriter.writerow(item) 112 | # instead of build the processed_data array 113 | processed_data.append(temp_row) 114 | 115 | try: 116 | # finally write all rows once a time to the output CSV. 117 | writer.writerows(processed_data) 118 | except BaseException as error: 119 | print(error) 120 | 121 | 122 | def geocode_address(geo_locator, line_address, component_restrictions=None, retry_counter=0): 123 | try: 124 | # if not using Google Map API For Work (Standard instead of Premium) you will raise an OVER_QUERY_LIMIT 125 | # due to the quotas request per seconds. So we have to sleep 500 ms between each request to Geocoding Service. 126 | if not GOOGLE_SECRET_KEY: 127 | time.sleep(0.5) 128 | 129 | # the geopy GoogleV3 geocoding call 130 | location = geo_locator.geocode(line_address, components=component_restrictions) 131 | 132 | if location is not None: 133 | # build a dict to append to output CSV 134 | location_result = {"Lat": location.latitude, "Long": location.longitude, "Error": "", 135 | "formatted_address": location.raw['formatted_address'], 136 | "location_type": location.raw['geometry']['location_type']} 137 | else: 138 | raise ValueError("None location found, please verify your address line") 139 | 140 | # To catch generic geocoder errors. TODO : Handle finer-grained exceptions 141 | except (ValueError, GeocoderQuotaExceeded, ConfigurationError, GeocoderParseError) as error: 142 | location_result = {"Lat": 0, "Long": 0, "Error": error.message, "formatted_address": "", "location_type": ""} 143 | 144 | # To retry because intermittent failures sometimes occurs 145 | except (GeocoderQueryError, GeocoderTimedOut) as error: 146 | if retry_counter < RETRY_COUNTER_CONST: 147 | return geocode_address(geo_locator, line_address, component_restrictions, retry_counter + 1) 148 | else: 149 | location_result = {"Lat": 0, "Long": 0, "Error": error.message, "formatted_address": "", 150 | "location_type": ""} 151 | 152 | print("address line : %s" % line_address) 153 | print("geocoded address : %s" % location_result["formatted_address"]) 154 | print("location type : %s" % location_result["location_type"]) 155 | 156 | return location_result 157 | 158 | 159 | if __name__ == '__main__': 160 | process_addresses_from_csv() 161 | -------------------------------------------------------------------------------- /python-samples/batchgeocoding/hairdresser_sample_addresses.csv: -------------------------------------------------------------------------------- 1 | country;name;enabled;addressline1;town;postalcode;IsoCode 2 | "AU";"Hairhouse Warehouse - Ipswich";"1";"Shop 28 Riverlink Shopping centre";"Ipswich";"4305";"AU" 3 | "AU";"Room For Hair";"0";"20 Jull Street";"ARMADALE";"6112";"AU" 4 | "AU";"D'luxe Hair & Beauty";"0";"Shop 4 Lot 6 Burra PlaceShellharbour City Plaza";"Shellharbour";"2529";"AU" 5 | "AU";"Complete Hair & Passion";"1";"Shop 10";"Cabramatta";"2166";"AU" 6 | "AU";"Kink The Art Of Hair - Orange - Closed";"0";"167 Summer Street";"Orange";"2800";"AU" 7 | "AU";"Bernadette Missen NLU - Closed";"0";"2B Raymond Street";"APPLECROSS";"6153";"AU" 8 | "AU";"Hairess - Closed";"0";"Murdoch University";"MURDOCH";"6150";"AU" 9 | "AU";"Rubi Hair Richmond - Closed";"0";"234 Swan St";"Richmond";"3121";"AU" 10 | "AU";"Hon Hair Studio";"0";"Shop 21/101-103 John street";"Cabramatta";"2166";"AU" 11 | "AU";"Kepnock Hair";"1";"Shop 7";"Bundaberg";"4670";"AU" 12 | "AU";"Nova Hair & Body";"1";"7 Walker Street";"MOUNT BARKER";"5251";"AU" 13 | "AU";"One Hair & Beauty";"1";"29 Wharf Street";"Forster";"2428";"AU" 14 | "AU";"Hairhouse Warehouse - Rosny - Closed";"0";"Shop U002";"Rosny";"7018";"AU" 15 | "AU";"Bliss Hair and Beauty";"0";"298 Bayliss Rd";"Heritage Park";"4118";"AU" 16 | "AU";"Volona & Associates - Ocean Keys";"0";"Shop 46 Ocean Keys Shopping Centre";"CLARKSON";"6030";"AU" 17 | "AU";"Glambox Hair Studio";"1";"Shop 5 226 Shute Harbour Road";"Cannonvale";"4802";"AU" 18 | "AU";"To Dye For Hair and Beauty";"0";"322 Brookfield Ave";"Broken Hill";"2880";"AU" 19 | "AU";"EV Hair & Beauty - Marrickville";"1";"Shop 82";"Marrickville";"2204";"AU" 20 | "ZA";"Nicci Phillips Hairdressing cc";"1";"23 Long Street";"Knysna";"6571";"ZA" 21 | -------------------------------------------------------------------------------- /python-samples/batchgeocoding_woosmap_localities/woosmap_localities_batch_geocoder.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | import requests 4 | from _csv import QUOTE_MINIMAL 5 | from csv import Dialect 6 | 7 | # used to set a google geocoding query by merging this value into one string with comma separated 8 | ADDRESS_COLUMNS_NAME = ["name", "AddressLine1", "AddressLine2", "AddressLine3", "City", "Postcode"] 9 | 10 | # used to define component restrictions for google geocoding 11 | COMPONENT_RESTRICTIONS_COLUMNS_NAME = {} 12 | 13 | # appended columns name to processed data csv 14 | NEW_COLUMNS_NAME = ["Lat", "Long", "Error", "formatted_address", "location_type"] 15 | 16 | # delimiter for input csv file 17 | DELIMITER = "," 18 | 19 | # Automatically retry X times when GeocoderErrors occur (sometimes the API Service return intermittent failures). 20 | RETRY_COUNTER_CONST = 5 21 | 22 | dir = os.path.dirname(__file__) 23 | 24 | # path and name for output csv file 25 | INPUT_CSV_FILE = os.path.join(dir, "input1.csv") 26 | 27 | # path and name for output csv file 28 | OUTPUT_CSV_FILE = os.path.join(dir, "processed_test.csv") 29 | 30 | # Add your Woosmap Private Key here 31 | WOOSMAP_PRIVATE_API_KEY = "XXXX" 32 | 33 | 34 | # dialect to manage different format of CSV 35 | class CustomDialect(Dialect): 36 | delimiter = DELIMITER 37 | quotechar = '"' 38 | doublequote = True 39 | skipinitialspace = False 40 | lineterminator = '\n' 41 | quoting = QUOTE_MINIMAL 42 | 43 | 44 | csv.register_dialect('ga', CustomDialect) 45 | 46 | 47 | class WoosmapLocalities: 48 | """A wrapper around the Woosmap Localities API.""" 49 | 50 | WOOSMAP_API_HOSTNAME = 'api.woosmap.com' 51 | 52 | def __init__(self): 53 | self.session = requests.Session() 54 | 55 | def get_details(self, public_id): 56 | return self.session.get( 57 | 'https://{hostname}/localities/details/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 58 | params={'private_key': WOOSMAP_PRIVATE_API_KEY, 59 | 'public_id': public_id}).json() 60 | 61 | def autocomplete(self, text_input): 62 | return self.session.get( 63 | 'https://{hostname}/localities/autocomplete/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 64 | params={'private_key': WOOSMAP_PRIVATE_API_KEY, 65 | 'input': text_input, 66 | 'types': 'address'}).json() 67 | 68 | def end(self): 69 | self.session.close() 70 | 71 | 72 | def process_addresses_from_csv(): 73 | woosmap_localities = WoosmapLocalities() 74 | with open(INPUT_CSV_FILE, 'r') as csvinput: 75 | with open(OUTPUT_CSV_FILE, 'w') as csvoutput: 76 | 77 | # new csv based on same dialect as input csv 78 | writer = csv.writer(csvoutput, dialect="ga") 79 | 80 | # create a proper header with stripped fieldnames for new CSV 81 | # header = [h.strip() for h in next(csvinput).split(DELIMITER)] 82 | 83 | # read Input CSV as Dict of Dict 84 | reader = csv.DictReader(csvinput, dialect="ga") 85 | 86 | # 2-dimensional data variable used to write the new CSV 87 | processed_data = [] 88 | 89 | # append new columns, to receive geocoded information, to the header of the new CSV 90 | header = list(reader.fieldnames) 91 | for column_name in NEW_COLUMNS_NAME: 92 | header.append(column_name.strip()) 93 | processed_data.append(header) 94 | 95 | # iterate through each row of input CSV 96 | for record in reader: 97 | # build a line address based on the merge of multiple field values to pass to Google Geocoder 98 | line_address = ','.join( 99 | str(val) for val in (record[column_name] for column_name in ADDRESS_COLUMNS_NAME)) 100 | 101 | # if you want to use componentRestrictions feature, 102 | # build a matching dict {'googleComponentRestrictionField' : 'yourCSVFieldValue'} 103 | # to pass to Google Geocoder 104 | component_restrictions = {} 105 | if COMPONENT_RESTRICTIONS_COLUMNS_NAME: 106 | for key, value in COMPONENT_RESTRICTIONS_COLUMNS_NAME.items(): 107 | component_restrictions[key] = record[value] 108 | 109 | # geocode the built line_address and passing optional componentRestrictions 110 | location = geocode_address(woosmap_localities, line_address, component_restrictions) 111 | 112 | # build a new temp_row for each csv entry to append to process_data Array 113 | # first, append existing fieldnames value to this temp_row 114 | temp_row = [record[column_name] for column_name in reader.fieldnames] 115 | 116 | # then, append geocoded field value to this temp_row 117 | for column_name in NEW_COLUMNS_NAME: 118 | try: 119 | temp_row.append(location[column_name]) 120 | except BaseException as error: 121 | print(error) 122 | temp_row.append('') 123 | 124 | # to manage more precisely errors, you could use csvwriter.writerow(item) 125 | # instead of build the processed_data array 126 | processed_data.append(temp_row) 127 | 128 | woosmap_localities.end() 129 | 130 | try: 131 | # finally write all rows once a time to the output CSV. 132 | writer.writerows(processed_data) 133 | except BaseException as error: 134 | print(error) 135 | 136 | 137 | def geocode_address(woosmap_localities, line_address, component_restrictions=None, retry_counter=0): 138 | try: 139 | suggestions = woosmap_localities.autocomplete(line_address) 140 | if suggestions is not None: 141 | location = woosmap_localities.get_details(suggestions['localities'][0].get('public_id', ''))['result'] 142 | if location is not None: 143 | location_geom = location.get('geometry') 144 | # build a dict to append to output CSV 145 | location_result = {"Lat": location['geometry']['location']['lat'], 146 | "Long": location['geometry']['location']['lng'], 147 | "Error": "", 148 | "formatted_address": location['formatted_address'], 149 | "location_type": location['geometry']['accuracy']} 150 | else: 151 | raise ValueError("None location found, please verify your address line") 152 | 153 | # To catch generic geocoder errors. TODO : Handle finer-grained exceptions 154 | except (ValueError) as error: 155 | location_result = {"Lat": 0, "Long": 0, "Error": error.message, "formatted_address": "", "location_type": ""} 156 | 157 | print("address line : %s" % line_address) 158 | print("geocoded address : %s" % location_result["formatted_address"]) 159 | print("location type : %s" % location_result["location_type"]) 160 | print("--------------------------------------------------------") 161 | 162 | return location_result 163 | 164 | 165 | if __name__ == '__main__': 166 | process_addresses_from_csv() 167 | -------------------------------------------------------------------------------- /python-samples/batchimport/README.md: -------------------------------------------------------------------------------- 1 | # Python Script to batch import your location to Woosmap Database. 2 | 3 | sample usage: 4 | 5 | python batch_import_locations.py 6 | 7 | The sample CSV File represent all the bars in Paris extracted from OpenStreetMap -------------------------------------------------------------------------------- /python-samples/batchimport/batch_import_locations.py: -------------------------------------------------------------------------------- 1 | import csv 2 | from _csv import QUOTE_ALL 3 | from csv import Dialect 4 | import requests 5 | 6 | endpoint_csv = 'france_museum_geocoded.csv' 7 | private_key = '' #your private key here 8 | endpoint_api = 'http://api.woosmap.com/stores' 9 | stores_batch_size = 100 10 | update_location = False 11 | 12 | 13 | class InvalidGeometry(Exception): 14 | pass 15 | 16 | 17 | def get_geometry(store): 18 | return { 19 | 'lat': store['latitude'], 20 | 'lng': store['longitude'] 21 | } 22 | 23 | 24 | def get_contact(store): 25 | return { 26 | 'website': store['SITWEB'] 27 | } 28 | 29 | 30 | def get_address(store): 31 | return { 32 | 'lines': [store['ADR']], 33 | 'city': store['VILLE'], 34 | 'zipcode': store['CP'], 35 | } 36 | 37 | 38 | def datagov2woosmap(store, id): 39 | geometry = get_geometry(store) 40 | address = get_address(store) 41 | contact = get_contact(store) 42 | return { 43 | 'storeId': id, 44 | 'name': store['NOM DU MUSEE'], 45 | 'address': address, 46 | 'contact': contact, 47 | 'location': geometry 48 | } 49 | 50 | 51 | class data_gov_dialect(Dialect): 52 | delimiter = ',' 53 | quotechar = '"' 54 | doublequote = True 55 | skipinitialspace = False 56 | lineterminator = '\n' 57 | quoting = QUOTE_ALL 58 | 59 | 60 | csv.register_dialect('dg', data_gov_dialect) 61 | 62 | 63 | def import_batch(batch, use_put=False): 64 | print('--> Importing batch (%d) locations' % len(batch)) 65 | if use_put: 66 | response = session.put(endpoint_api, 67 | params={'private_key': private_key}, 68 | json={'stores': batch}) 69 | else: 70 | response = session.post(endpoint_api, 71 | params={'private_key': private_key}, 72 | json={'stores': batch}) 73 | 74 | print('<-- Total time:', response.elapsed.total_seconds()) 75 | if response.status_code >= 400: 76 | print('Failed batch') 77 | print(response.text) 78 | return False 79 | 80 | return True 81 | 82 | 83 | if __name__ == '__main__': 84 | with open(endpoint_csv, 'r') as f: 85 | reader = csv.DictReader(f, dialect="dg") 86 | 87 | failed = [] 88 | session = requests.Session() 89 | if not update_location: 90 | response = session.delete(endpoint_api, params={'private_key': private_key}) 91 | 92 | if response.status_code >= 400: 93 | print(response.text) 94 | exit(-1) 95 | 96 | batch = [] 97 | batch_size = stores_batch_size 98 | id = 0 99 | for location in reader: 100 | id += 1 101 | try: 102 | woosmap_location = datagov2woosmap(location, "ID" + str(id)) 103 | 104 | if len(batch) == batch_size: 105 | batch_result = import_batch(batch, use_put=update_location) 106 | batch = [] 107 | else: 108 | batch.append(woosmap_location) 109 | 110 | except InvalidGeometry: 111 | pass 112 | 113 | if batch: 114 | batch_result = import_batch(batch, use_put=update_location) 115 | batch = [] 116 | -------------------------------------------------------------------------------- /python-samples/csv_to_woosmap/csv_to_woosmap.py: -------------------------------------------------------------------------------- 1 | import unicodecsv as csv 2 | import json 3 | import os 4 | import time 5 | import requests 6 | from hashlib import sha1 7 | 8 | YOUR_INPUT_CSV_FILE = 'foodmarkets.csv' 9 | WOOSMAP_PRIVATE_API_KEY = '23713926-1af5-4321-ba54-032966f6e95d' 10 | BATCH_SIZE = 5 11 | 12 | 13 | class MyCSVDialect(csv.Dialect): 14 | delimiter = ',' 15 | quotechar = '"' 16 | doublequote = True 17 | skipinitialspace = False 18 | lineterminator = '\n' 19 | quoting = csv.QUOTE_ALL 20 | 21 | 22 | class Woosmap: 23 | """A wrapper around the Woosmap Data API.""" 24 | 25 | WOOSMAP_API_HOSTNAME = 'api.woosmap.com' 26 | 27 | def __init__(self): 28 | self.session = requests.Session() 29 | 30 | def delete(self): 31 | self.session.delete('https://{hostname}/stores/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 32 | params={'private_key': WOOSMAP_PRIVATE_API_KEY}) 33 | 34 | def post(self, payload): 35 | return self.session.post('https://{hostname}/stores/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 36 | params={'private_key': WOOSMAP_PRIVATE_API_KEY}, 37 | json={'stores': payload}) 38 | 39 | def end(self): 40 | self.session.close() 41 | 42 | 43 | def get_name(asset): 44 | name = asset.get('Name', '') 45 | if name: 46 | return name 47 | else: 48 | raise ValueError('Unable to get the Name') 49 | 50 | 51 | def generate_id(asset): 52 | asset_id = sha1(get_name(asset).encode('utf-8')).hexdigest() 53 | return asset_id 54 | 55 | 56 | def get_contact(asset): 57 | return { 58 | 'website': asset.get('Website', ''), 59 | 'phone': asset.get('Contact Phone', ''), 60 | 'email': asset.get('Contact Email', '') 61 | } 62 | 63 | 64 | def get_geometry(asset): 65 | latitude = asset.get('Latitude', None) 66 | longitude = asset.get('Longitude', None) 67 | if latitude is not None and longitude is not None: 68 | return { 69 | 'lat': float(latitude), 70 | 'lng': float(longitude) 71 | } 72 | else: 73 | raise ValueError('Unable to get the location') 74 | 75 | 76 | def get_address(asset): 77 | return { 78 | 'lines': [asset.get('Address Line', '')], 79 | 'city': asset.get('City', ''), 80 | 'zipcode': asset.get('Zipcode', '') 81 | } 82 | 83 | 84 | def convert_to_woosmap(asset): 85 | converted_asset = {} 86 | try: 87 | converted_asset.update({ 88 | 'storeId': generate_id(asset), 89 | 'name': get_name(asset), 90 | 'address': get_address(asset), 91 | 'contact': get_contact(asset), 92 | 'location': get_geometry(asset) 93 | }) 94 | except ValueError as ve: 95 | print('ValueError Raised {0} for Asset {1}'.format(ve, json.dumps(asset, indent=2))) 96 | 97 | return converted_asset 98 | 99 | 100 | def import_assets(assets_data, woosmap_api_helper): 101 | try: 102 | print('Batch import {count} Assets...'.format(count=len(assets_data))) 103 | response = woosmap_api_helper.post(assets_data) 104 | if response.status_code >= 400: 105 | response.raise_for_status() 106 | 107 | except requests.exceptions.HTTPError as http_exception: 108 | if http_exception.response.status_code >= 400: 109 | print('Woosmap API Import Error: {0}'.format(http_exception.response.text)) 110 | else: 111 | print('Error requesting the API: {0}'.format(http_exception)) 112 | return False 113 | except Exception as exception: 114 | print('Failed importing Assets! {0}'.format(exception)) 115 | return False 116 | 117 | print('Successfully imported in {0} seconds'.format(response.elapsed.total_seconds())) 118 | return True 119 | 120 | 121 | def batch(assets_data, n=1): 122 | l = len(assets_data) 123 | for ndx in range(0, l, n): 124 | yield assets_data[ndx:min(ndx + n, l)] 125 | 126 | 127 | def main(): 128 | start = time.time() 129 | print('Start parsing and importing your data...') 130 | with open(file_path, 'rb') as csv_file: 131 | try: 132 | reader = csv.DictReader(csv_file, dialect=MyCSVDialect()) 133 | woosmap_assets = [] 134 | for asset in reader: 135 | converted_asset = convert_to_woosmap(asset) 136 | if bool(converted_asset): 137 | woosmap_assets.append(converted_asset) 138 | 139 | print('{0} Assets converted from source file'.format(len(woosmap_assets))) 140 | 141 | woosmap_api_helper = Woosmap() 142 | # /!\ deleting existing assets before posting new ones /!\ 143 | woosmap_api_helper.delete() 144 | 145 | count_imported_assets = 0 146 | for chunk in batch(woosmap_assets, BATCH_SIZE): 147 | imported_success = import_assets(chunk, woosmap_api_helper) 148 | if imported_success: 149 | count_imported_assets += len(chunk) 150 | 151 | woosmap_api_helper.end() 152 | print("{0} Assets successfully imported".format(count_imported_assets)) 153 | 154 | except csv.Error as csv_error: 155 | print('Error in CSV file found: {0}'.format(csv_error)) 156 | except Exception as exception: 157 | print("Script Failed! {0}".format(exception)) 158 | finally: 159 | end = time.time() 160 | print('...Script ended in {0} seconds'.format(end - start)) 161 | 162 | 163 | if __name__ == '__main__': 164 | file_path = os.path.join(os.getcwd(), YOUR_INPUT_CSV_FILE) 165 | if os.path.exists(file_path): 166 | main() 167 | else: 168 | print('File not found: {0} '.format(file_path)) 169 | -------------------------------------------------------------------------------- /python-samples/csv_to_woosmap/foodmarkets.csv: -------------------------------------------------------------------------------- 1 | Type,Latitude,Longitude,Name,Address Line,City,Zipcode,Website,Contact Phone,Contact Email 2 | covered,51.919948,4.486843,Markthal Rotterdam,Dominee Jan Scharpstraat 298,Rotterdam,3011 GZ,http://markthalrotterdam.nl/,+31 (0)30 234 64 64,info@markthalrotterdam.nl 3 | covered,41.877657,12.473909,Testaccio Market,Via Galvani/Via Alessandro Volta,Roma,00118,http://www.mercatotestaccio.com/,+39 06 578 0638,info@mercatotestaccio.ocm 4 | covered,48.199044,16.364234,Naschmarkt Vienna,Via Galvani/Via Alessandro Volta,Vienna,1060,http://www.naschmarkt-vienna.com/,+43 1 240555,contact@naschmarkt-vienna.com 5 | covered,40.415261,-3.708944,Mercado de San Miguel,Plaza de San Miguel,Madrid,28005,http://www.mercadodesanmiguel.es/en,(+34) 915 42 49 36,administracion@mercadodesanmiguel.es 6 | covered,41.381635,2.171596,Mercado de La Boqueria,"La Rambla, 91",Barcelona,08001,http://www.boqueria.info/,+34 933 18 25 84,administracion@mercadodesanmiguel.es 7 | covered,43.695530,7.275492,Cours Saleya,Place Charles Félix,Nice,06300,http://leblogduvieuxnice.nicematin.com/.services/blog/6a0120a864ed46970b0162fd89fc17970d/search?filter.q=marché,, 8 | covered,48.849021,2.377700,Marché d’Aligre,Place d’Aligre,Paris,75012,http://equipement.paris.fr/marche-couvert-beauvau-marche-d-aligre-5480,01 45 11 71 11, 9 | covered,43.776540,11.253133,Mercato Centrale di San Lorenzo,Piazza del Mercato Centrale,Firenze,50123,http://www.mercatocentrale.it,+39 0552399798,info@mercatocentrale.it 10 | covered,55.684025,12.569469,Torvehallerne,Frederiksborggade 21,Copenhagen,1360,http://torvehallernekbh.dk,+39 0552399798,info@torvehallernekbh.dk 11 | covered,51.505046,-0.090679,Borough Market,8 Southwark St,London,SE1 1TL,http://boroughmarket.org.uk,020 7407 1002, 12 | covered,48.135105,11.576246,Victuals Market,Viktualienmarkt 3,München,80881,http://www.muenchen.de/int/en/shopping/markets/viktualienmarkt.html,+49 89 89068205, 13 | covered,43.40214,3.69545,Halles de Sète,Rue Gambetta,Sète,34200,http://www.halles-sete.com/,04 99 04 70 00,commerce-artisanat@ville-sete.fr 14 | outside,44.011821,4.418889,Marché d'Uzès,Place aux Herbes,Uzès,30700,http://www.uzes.fr/Calendrier-des-marches-brocantes-et-foires_a126.html,+33 (0)4 66 22 68 88, 15 | outside,43.876503,5.393588,Le Grand Marché d'Apt,Place de la Bouquerie,Apt,84400,http://www.luberon-apt.fr/index.php/fr/sortir/les-marches,+33 (0)4 90 74 03 18,oti@paysapt-luberon.fr 16 | outside,46.187465,-1.327086,Le Marché de la Flotte,Rue du Marché,La Flotte,17630,http://laflotte.fr/index.php/Vie-quotidienne/les-marches.html,05.46.09.15.00,annie@laflotte.fr 17 | outside,55.947817,-3.203562,Edimburgh Farmers' Market,Castle Terrace,Edimburgh,EH1 UK,http://www.edinburghfarmersmarket.co.uk/,0131 220 8580, 18 | covered,54.596073,-5.921654,St George's Market,12 - 20 East Bridge Street,Belfast,BT1 3NQ,http://www.belfastcity.gov.uk/tourism-venues/stgeorgesmarket/stgeorgesmarket-index.aspx,028 9043 5704,markets@belfastcity.gov.uk 19 | covered,50.626710,3.049325,Halles de Wazemmes,Place de la nouvelle aventure,Lille,59000,http://www.halles-wazemmes.com/,, 20 | -------------------------------------------------------------------------------- /python-samples/excel_to_woosmap/excel_to_woosmap.py: -------------------------------------------------------------------------------- 1 | from openpyxl import load_workbook 2 | import json 3 | import os 4 | import time 5 | import requests 6 | from hashlib import sha1 7 | 8 | INPUT_EXCEL_FILE = 'foodmarkets.xlsx' 9 | WORKSHEET_NAME = 'foodmarkets' 10 | WOOSMAP_PRIVATE_API_KEY = '23713926-1af5-4321-ba54-032966f6e95d' 11 | BATCH_SIZE = 5 12 | 13 | 14 | class Woosmap: 15 | """A wrapper around the Woosmap Data API.""" 16 | 17 | WOOSMAP_API_HOSTNAME = 'api.woosmap.com' 18 | 19 | def __init__(self): 20 | self.session = requests.Session() 21 | 22 | def delete(self): 23 | self.session.delete('https://{hostname}/stores/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 24 | params={'private_key': WOOSMAP_PRIVATE_API_KEY}) 25 | 26 | def post(self, payload): 27 | return self.session.post('https://{hostname}/stores/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 28 | params={'private_key': WOOSMAP_PRIVATE_API_KEY}, 29 | json={'stores': payload}) 30 | 31 | def end(self): 32 | self.session.close() 33 | 34 | 35 | class ExcelFile: 36 | """A simple wrapper around the needed openpyxl functions for this script""" 37 | 38 | def __init__(self, filename, worksheet_name=''): 39 | self.filename = filename 40 | self.workbook = load_workbook(self.filename) 41 | self.worksheet_name = worksheet_name if worksheet_name else self.get_first_worksheet_name() 42 | self.worksheet = self.workbook.get_sheet_by_name(self.worksheet_name) 43 | 44 | def get_first_worksheet_name(self): 45 | return self.workbook.get_sheet_names()[0] 46 | 47 | def iter_rows(self): 48 | for row in self.worksheet.iter_rows(): 49 | yield [cell.value for cell in row] 50 | 51 | 52 | def get_name(asset): 53 | name = asset.get('Name', '') 54 | if name: 55 | return name 56 | else: 57 | raise ValueError('Unable to get the Name') 58 | 59 | 60 | def generate_id(asset): 61 | asset_id = sha1(get_name(asset).encode('utf-8')).hexdigest() 62 | return asset_id 63 | 64 | 65 | def get_contact(asset): 66 | return { 67 | 'website': asset.get('Website', ''), 68 | 'phone': asset.get('Contact Phone', ''), 69 | 'email': asset.get('Contact Email', '') 70 | } 71 | 72 | 73 | def get_geometry(asset): 74 | latitude = asset.get('Latitude', None) 75 | longitude = asset.get('Longitude', None) 76 | if latitude is not None and longitude is not None: 77 | return { 78 | 'lat': float(latitude), 79 | 'lng': float(longitude) 80 | } 81 | else: 82 | raise ValueError('Unable to get the location') 83 | 84 | 85 | def get_address(asset): 86 | return { 87 | 'lines': [asset.get('Address Line', '')], 88 | 'city': asset.get('City', ''), 89 | 'zipcode': asset.get('Zipcode', '') 90 | } 91 | 92 | 93 | def convert_to_woosmap(asset): 94 | converted_asset = {} 95 | try: 96 | converted_asset.update({ 97 | 'storeId': generate_id(asset), 98 | 'name': get_name(asset), 99 | 'address': get_address(asset), 100 | 'contact': get_contact(asset), 101 | 'location': get_geometry(asset) 102 | }) 103 | except ValueError as ve: 104 | print('ValueError Raised {0} for Asset {1}'.format(ve, json.dumps(asset, indent=2))) 105 | 106 | return converted_asset 107 | 108 | 109 | def import_assets(assets_data, woosmap_api_helper): 110 | try: 111 | print('Batch import {count} Assets...'.format(count=len(assets_data))) 112 | response = woosmap_api_helper.post(assets_data) 113 | if response.status_code >= 400: 114 | response.raise_for_status() 115 | 116 | except requests.exceptions.HTTPError as http_exception: 117 | if http_exception.response.status_code >= 400: 118 | print('Woosmap API Import Error: {0}'.format(http_exception.response.text)) 119 | else: 120 | print('Error requesting the API: {0}'.format(http_exception)) 121 | return False 122 | except Exception as exception: 123 | print('Failed importing Assets! {0}'.format(exception)) 124 | return False 125 | 126 | print('Successfully imported in {0} seconds'.format(response.elapsed.total_seconds())) 127 | return True 128 | 129 | 130 | def batch(assets_data, n=1): 131 | l = len(assets_data) 132 | for ndx in range(0, l, n): 133 | yield assets_data[ndx:min(ndx + n, l)] 134 | 135 | 136 | def main(): 137 | start = time.time() 138 | print('Start parsing and importing your data...') 139 | excel_file = ExcelFile(INPUT_EXCEL_FILE, WORKSHEET_NAME) 140 | sheet_data = list(excel_file.iter_rows()) 141 | header = sheet_data.pop(0) 142 | assets_as_dict = [dict(zip(header, item)) for item in sheet_data] 143 | 144 | woosmap_assets = [] 145 | for asset in assets_as_dict: 146 | converted_asset = convert_to_woosmap(asset) 147 | if bool(converted_asset): 148 | woosmap_assets.append(converted_asset) 149 | 150 | print('{0} Assets converted from source file'.format(len(woosmap_assets))) 151 | 152 | woosmap_api_helper = Woosmap() 153 | # /!\ deleting existing assets before posting new ones /!\ 154 | woosmap_api_helper.delete() 155 | 156 | count_imported_assets = 0 157 | for chunk in batch(woosmap_assets): 158 | imported_success = import_assets(chunk, woosmap_api_helper) 159 | if imported_success: 160 | count_imported_assets += len(chunk) 161 | 162 | woosmap_api_helper.end() 163 | 164 | end = time.time() 165 | print('...Script ended in {0} seconds'.format(end - start)) 166 | 167 | 168 | if __name__ == '__main__': 169 | file_path = os.path.join(os.getcwd(), INPUT_EXCEL_FILE) 170 | if os.path.exists(file_path): 171 | main() 172 | else: 173 | print('File not found: {0} '.format(file_path)) 174 | -------------------------------------------------------------------------------- /python-samples/excel_to_woosmap/foodmarkets.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Woosmap/woosmap-samples/dad13508d36ecd85204c9027c4c9848c3b4ed087/python-samples/excel_to_woosmap/foodmarkets.xlsx -------------------------------------------------------------------------------- /python-samples/googlemybusiness_to_woosmap/googlemybusiness_to_woosmap.py: -------------------------------------------------------------------------------- 1 | import httplib2 2 | import json 3 | import os 4 | import requests 5 | 6 | from apiclient import discovery 7 | from oauth2client.client import flow_from_clientsecrets 8 | from oauth2client.file import Storage 9 | from oauth2client import tools 10 | 11 | from timezonefinder import TimezoneFinder 12 | 13 | tf = TimezoneFinder() 14 | 15 | WOOSMAP_PRIVATE_API_KEY = 'eafb8805-3743-4cb9-abff-xxxxxxxxxxxx' 16 | 17 | GOOGLE_ACCOUNT_NAME = "accounts/123456789101112131415" # The Account Manager 18 | GOOGLE_CREDENTIALS_PATH = "client_secret_xxxxxxx-xxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com.json" 19 | 20 | 21 | class GoogleMyBusiness(object): 22 | """A wrapper around the Google My Business API.""" 23 | 24 | API_NAME = 'mybusiness' 25 | API_VERSION = 'v3' 26 | DISCOVERY_URI = 'https://developers.google.com/my-business/samples/{api}_google_rest_{apiVersion}.json' 27 | SCOPE = "https://www.googleapis.com/auth/plus.business.manage" 28 | REDIRECT_URI = 'http://localshot:8080' 29 | 30 | def __init__(self, credentials_path, account_name=''): 31 | self.credentials_path = credentials_path 32 | self.account_name = account_name 33 | self.credentials = self.get_credentials() 34 | self.http = self.credentials.authorize(httplib2.Http()) 35 | if self.credentials is not None and self.credentials.access_token_expired: 36 | self.credentials.refresh(self.http) 37 | 38 | self.service = self.build_service() 39 | if not self.account_name: 40 | # If GOOGLE_ACCOUNT_NAME constant is not set, we pick the first in account listing 41 | self.account_name = self.list_accounts()['accounts'][0]['name'] 42 | 43 | def build_service(self): 44 | return discovery.build(self.API_NAME, self.API_VERSION, http=self.http, 45 | discoveryServiceUrl=self.DISCOVERY_URI) 46 | 47 | def get_credentials(self): 48 | storage_path = '.' + os.path.splitext(os.path.basename(__file__))[0] + '.credentials' 49 | storage = Storage(storage_path) 50 | 51 | credentials = storage.get() if os.path.exists(storage_path) else None 52 | 53 | if credentials is None or credentials.invalid: 54 | flow = flow_from_clientsecrets(self.credentials_path, 55 | scope=self.SCOPE, 56 | redirect_uri=self.REDIRECT_URI) 57 | 58 | flow.params['access_type'] = 'offline' 59 | flow.params['approval_prompt'] = 'force' 60 | credentials = tools.run_flow(flow, storage) 61 | 62 | return credentials 63 | 64 | def list_accounts(self): 65 | return self.service.accounts().list().execute() 66 | 67 | def list_locations(self): 68 | return self.service.accounts().locations().list(name=self.account_name).execute() 69 | 70 | 71 | class Woosmap: 72 | """A wrapper around the Woosmap Data API.""" 73 | 74 | WOOSMAP_API_HOSTNAME = 'api.woosmap.com' 75 | 76 | def __init__(self): 77 | self.session = requests.Session() 78 | 79 | def delete(self): 80 | self.session.delete('https://{hostname}/stores/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 81 | params={'private_key': WOOSMAP_PRIVATE_API_KEY}) 82 | 83 | def post(self, payload): 84 | return self.session.post('https://{hostname}/stores/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 85 | params={'private_key': WOOSMAP_PRIVATE_API_KEY}, 86 | json={'stores': payload}) 87 | 88 | def end(self): 89 | self.session.close() 90 | 91 | 92 | def get_name(asset): 93 | return asset.get('locationName') 94 | 95 | 96 | def get_id(asset): 97 | return asset.get('name').rsplit('/', 1)[1] 98 | 99 | 100 | def get_contact(asset): 101 | return { 102 | 'website': asset.get('websiteUrl', ''), 103 | 'phone': asset.get('primaryPhone', '') 104 | } 105 | 106 | 107 | def get_tags(asset): 108 | return asset.get('labels', []) 109 | 110 | 111 | def get_primary_category(asset): 112 | primary_type = [] 113 | primary_category = asset.get('primaryCategory', {}) 114 | if primary_category: 115 | primary_type.append(primary_category.get('name', '')) 116 | 117 | return primary_type 118 | 119 | 120 | def get_additional_categories(asset): 121 | additional_types = [] 122 | additional_categories = asset.get('additionalCategories', []) 123 | if additional_categories: 124 | for category in additional_categories: 125 | additional_types.append(category.get('name', '')) 126 | 127 | return additional_types 128 | 129 | 130 | def get_types(asset): 131 | types = [] 132 | types.extend(get_primary_category(asset)) 133 | types.extend(get_additional_categories(asset)) 134 | return types 135 | 136 | 137 | def get_user_properties(asset): 138 | # return all other useful attributes to store in "userProperties" property 139 | return { 140 | 'photos': asset.get('photos', {}), 141 | 'metadata': asset.get('metadata', {}), 142 | 'storeCode': asset.get('storeCode', ''), 143 | 'languageCode': asset.get('languageCode', ''), 144 | 'attributes': asset.get('attributes', []), 145 | 'serviceArea': asset.get('serviceArea', {}), 146 | 'locationKey': asset.get('locationKey', {}), 147 | 'priceLists': asset.get('priceLists', []), 148 | 'locationState': asset.get('locationState', {}), 149 | 'additionalPhones': asset.get('locationState', []), 150 | 'adWordsLocationExtensions': asset.get('adWordsLocationExtensions', {}), 151 | } 152 | 153 | 154 | def get_geometry(asset): 155 | # latlng can be empty {} if you did'nt moved the pushpin location was created on Google 156 | # Thus you need to geocode address location to get lat/lng 157 | latlng = asset.get('latlng', {}) 158 | if latlng: 159 | return { 160 | 'lat': latlng.get('latitude'), 161 | 'lng': latlng.get('longitude') 162 | } 163 | else: 164 | # TODO: geocode address 165 | raise ValueError('Unable to get the latlng') 166 | 167 | 168 | def get_address(asset): 169 | address = asset.get('address', {}) 170 | if address: 171 | return { 172 | 'lines': address.get('addressLines', []), 173 | 'city': address.get('locality', ''), 174 | 'zipcode': address.get('postalCode', ''), 175 | 'countryCode': address.get('country', '') 176 | } 177 | else: 178 | raise ValueError('Unable to get the Address') 179 | 180 | 181 | def find_timezone(asset): 182 | latlng = get_geometry(asset) 183 | timezone_name = '' 184 | try: 185 | lat = float(latlng['lat']) 186 | lng = float(latlng['lng']) 187 | timezone_name = tf.timezone_at(lng=lng, lat=lat) 188 | if timezone_name is None: 189 | timezone_name = tf.closest_timezone_at(lng=lng, lat=lat) 190 | return timezone_name 191 | 192 | except ValueError: 193 | print('Unable to Get the timezone for {latlng}'.format(latlng=latlng)) 194 | timezone_name = 'Europe/Paris' 195 | 196 | finally: 197 | return {'timezone': timezone_name} 198 | 199 | 200 | def get_regular_hours(asset): 201 | # TODO: manage regular hours that take longer than 24hours (e.g. open monday 9am and close Tuesday 9pm) 202 | regular_hours = asset.get('regularHours', {}) 203 | periods = regular_hours.get('periods', []) 204 | 205 | usual = {} 206 | week_days = [{'MONDAY': '1'}, {'TUESDAY': '2'}, {'WEDNESDAY': '3'}, {'THURSDAY': '4'}, {'FRIDAY': '5'}, 207 | {'SATURDAY': '6'}, 208 | {'SUNDAY': '7'}] 209 | if periods: 210 | for period in periods: 211 | for day in week_days: 212 | for key in day: 213 | if period['openDay'] == key: 214 | usual.setdefault(day[key], []).append({'start': period['openTime'], 'end': period['closeTime']}) 215 | 216 | return {'usual': usual} 217 | 218 | 219 | def get_special_hours(asset): 220 | # TODO: manage special hours that take longer than 24hours (e.g. open monday 9am and close Tuesday 9pm) 221 | special_hours = asset.get('specialHours', {}) 222 | periods = special_hours.get('specialHourPeriods', []) 223 | 224 | special = {} 225 | if periods: 226 | for period in periods: 227 | start_date = period.get('startDate', '') 228 | if start_date: 229 | key = str(start_date.get('year')) + '-' + str(start_date.get('month')) + '-' + str( 230 | start_date.get('day')) 231 | if period.get('isClosed', False): 232 | special.setdefault(key, []) 233 | else: 234 | special.setdefault(key, []).append({'start': period['openTime'], 'end': period['closeTime']}) 235 | 236 | return {'special': special} 237 | 238 | 239 | def get_hours(asset): 240 | return dict(find_timezone(asset).items() + get_regular_hours(asset).items() + get_special_hours(asset).items()) 241 | 242 | 243 | def convert_mybusiness_to_woosmap(data): 244 | converted_asset = {} 245 | try: 246 | converted_asset.update({ 247 | 'storeId': get_id(data), 248 | 'name': get_name(data), 249 | 'address': get_address(data), 250 | 'contact': get_contact(data), 251 | 'location': get_geometry(data), 252 | 'openingHours': get_hours(data), 253 | 'tags': get_tags(data), 254 | 'types': get_types(data), 255 | 'userProperties': get_user_properties(data) 256 | }) 257 | except ValueError as ve: 258 | print('ValueError Raised {0} for MyBusiness location {1}'.format(ve, json.dumps(data, indent=2))) 259 | 260 | return converted_asset 261 | 262 | 263 | def import_assets(assets_data, woosmap_api): 264 | try: 265 | print('Batch import {count} Assets to Woosmap...'.format(count=len(assets_data))) 266 | response = woosmap_api.post(assets_data) 267 | if response.status_code >= 400: 268 | response.raise_for_status() 269 | 270 | except requests.exceptions.HTTPError as http_exception: 271 | if http_exception.response.status_code >= 400: 272 | print('Woosmap API Import Error: {0}'.format(http_exception.response.text)) 273 | else: 274 | print('Error requesting the API: {0}'.format(http_exception)) 275 | return False 276 | except Exception as exception: 277 | print('Failed importing Assets! {0}'.format(exception)) 278 | return False 279 | 280 | print('Successfully imported in {0} seconds'.format(response.elapsed.total_seconds())) 281 | return True 282 | 283 | 284 | def batch(assets_data, n=1): 285 | l = len(assets_data) 286 | for ndx in range(0, l, n): 287 | yield assets_data[ndx:min(ndx + n, l)] 288 | 289 | 290 | def main(): 291 | google_my_business = GoogleMyBusiness(GOOGLE_CREDENTIALS_PATH, GOOGLE_ACCOUNT_NAME) 292 | extracted_my_business = google_my_business.list_locations() 293 | woosmap_assets = [] 294 | for location in extracted_my_business["locations"]: 295 | converted_asset = convert_mybusiness_to_woosmap(location) 296 | if bool(converted_asset): 297 | woosmap_assets.append(converted_asset) 298 | 299 | woosmap_api = Woosmap() 300 | woosmap_api.delete() 301 | 302 | for chunk in batch(woosmap_assets): 303 | import_assets(chunk, woosmap_api) 304 | 305 | 306 | if __name__ == '__main__': 307 | main() 308 | -------------------------------------------------------------------------------- /python-samples/googleplaces_to_woosmap/googleplaces_to_woosmap.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import time 3 | from hashlib import sha1 4 | 5 | import simplejson as json # useful to deal with Decimal(x.x) potential errors 6 | from googleplaces import GooglePlaces, GooglePlacesAttributeError, GooglePlacesError 7 | from timezonefinder import TimezoneFinder 8 | 9 | GOOGLE_API_KEY = 'AIzaSyDRcaVMH1F_H3pIbm1T-XXXXXXXXXXX' 10 | WOOSMAP_OUTPUT_JSON = 'woosmap_output.json' 11 | SEARCH_DATA_PATH = 'search_data.json' 12 | 13 | tf = TimezoneFinder() 14 | 15 | 16 | def get_location(places_location): 17 | return { 18 | 'lat': float(places_location['geometry']['location']['lat']), 19 | 'lng': float(places_location['geometry']['location']['lng']) 20 | } 21 | 22 | 23 | def get_id(places_location): 24 | return sha1(places_location.get('place_id')).hexdigest() 25 | 26 | 27 | def find_timezone(places_location): 28 | latlng = get_location(places_location) 29 | timezone_name = '' 30 | try: 31 | lat = latlng['lat'] 32 | lng = latlng['lng'] 33 | timezone_name = tf.timezone_at(lng=lng, lat=lat) 34 | if timezone_name is None: 35 | timezone_name = tf.closest_timezone_at(lng=lng, lat=lat) 36 | return timezone_name 37 | 38 | except ValueError: 39 | print('Unable to Get the timezone for {latlng}'.format(latlng=latlng)) 40 | timezone_name = 'Europe/Paris' 41 | 42 | finally: 43 | return {'timezone': timezone_name} 44 | 45 | 46 | # TODO : Update for multi opening and closing in a day 47 | def get_regular_hours(places_location): 48 | weekdays = [1, 2, 3, 4, 5, 6, 0] 49 | day_index = 1 50 | usual = {} 51 | day_hours = places_location.get('opening_hours', {}) 52 | 53 | if bool(day_hours): 54 | try: 55 | for day in weekdays: 56 | hours = [] 57 | start_hour = '' 58 | end_hour = '' 59 | if len(day_hours['periods']) == 1: 60 | hours.append({'all-day': True}) 61 | else: 62 | for period in day_hours['periods']: 63 | if period['open']['day'] and period['open']['day'] == day: 64 | start_hour = period['open']['time'][:2] + ':' + period['open']['time'][-2:] 65 | if period['close']: 66 | end_hour = period['close']['time'][:2] + ':' + period['close']['time'][-2:] 67 | break 68 | if start_hour and end_hour: 69 | hours.append({'start': start_hour, 'end': end_hour}) 70 | 71 | usual[day_index] = hours 72 | day_index += 1 73 | 74 | except Exception as error: 75 | raise ValueError('Unable to get the OpeningHours: {0}'.format(error)) 76 | 77 | return {'usual': usual} 78 | 79 | 80 | def get_contact(places_location): 81 | website = places_location.get('website', '') if places_location.get('website', '') else places_location.get('url') 82 | return { 83 | 'website': website, 84 | 'phone': places_location.get('formatted_phone_number') 85 | } 86 | 87 | 88 | def get_address(places_location): 89 | return { 90 | 'lines': [places_location.get('formatted_address')] 91 | } 92 | 93 | 94 | def get_name(places_location): 95 | return places_location.get('name') 96 | 97 | 98 | def get_types(places_location): 99 | return places_location.get('types', []) 100 | 101 | 102 | def get_hours(places_location): 103 | return dict(find_timezone(places_location).items() + get_regular_hours(places_location).items()) 104 | 105 | 106 | def google_places_to_woosmap(places_location): 107 | converted_asset = {} 108 | try: 109 | converted_asset.update({ 110 | 'storeId': get_id(places_location), 111 | 'name': get_name(places_location), 112 | 'address': get_address(places_location), 113 | 'contact': get_contact(places_location), 114 | 'location': get_location(places_location), 115 | 'openingHours': get_hours(places_location), 116 | 'types': get_types(places_location) 117 | }) 118 | except ValueError as ve: 119 | print('ValueError Raised {0} for Places Location {1}'.format(ve, json.dumps(places_location, indent=2))) 120 | 121 | return converted_asset 122 | 123 | 124 | def export_to_woosmap_json(input_json): 125 | data_places = {'stores': input_json} 126 | with codecs.open(WOOSMAP_OUTPUT_JSON, 'w', encoding='utf8') as outfile: 127 | json.dump(data_places, outfile, indent=2, ensure_ascii=False) 128 | 129 | 130 | def main(): 131 | woosmap_converted_asset = [] 132 | with codecs.open(SEARCH_DATA_PATH, 'rb', encoding='utf8') as search_data_file: 133 | search_data = json.loads(search_data_file.read()) 134 | google_places = GooglePlaces(GOOGLE_API_KEY) 135 | for place_id in search_data['places_ids']: 136 | try: 137 | place = google_places.get_place(place_id) 138 | converted_asset = google_places_to_woosmap(place.details) 139 | if bool(converted_asset): 140 | print("... {place_name} ...converted to Wosmap OK".format(place_name=place.name.encode('utf-8'))) 141 | woosmap_converted_asset.append(converted_asset) 142 | 143 | except (GooglePlacesError, GooglePlacesAttributeError) as error_detail: 144 | print('Google Returned an Error : {0} for Place ID : {1}'.format(error_detail, place_id)) 145 | pass 146 | 147 | except Exception as exception: 148 | print('Exception Returned {0} for Place ID : {1}'.format(exception, place_id)) 149 | time.sleep(1) 150 | pass 151 | 152 | export_to_woosmap_json(woosmap_converted_asset) 153 | print('{0} google places extracted for {1} places_ids found '.format(len(woosmap_converted_asset), 154 | len(search_data['places_ids']))) 155 | 156 | 157 | if __name__ == '__main__': 158 | main() 159 | -------------------------------------------------------------------------------- /python-samples/googleplaces_to_woosmap/search_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "places_ids": [ 3 | "ChIJC4Wu9QmvthIRDmojjnJB6rA", 4 | "ChIJuRFUv33Ew0cRk0JQb2xQOfs", 5 | "ChIJ9yGYrIwd9kcRlV-tE8ixHpw", 6 | "ChIJJ9NzTOW83UcRh3KE1nh9bno", 7 | "ChIJNx8atyPbEEgRXZQc7nPAKpQ", 8 | "ChIJWRTTPgJl5kcROB_8Pwj9BeI" 9 | ] 10 | } -------------------------------------------------------------------------------- /python-samples/googlesheet_to_woosmap/googlesheet_to_woosmap.py: -------------------------------------------------------------------------------- 1 | import httplib2 2 | import os 3 | import json 4 | import requests 5 | from hashlib import sha1 6 | 7 | from apiclient import discovery 8 | from oauth2client.client import flow_from_clientsecrets 9 | from oauth2client import tools 10 | from oauth2client.file import Storage 11 | 12 | GOOGLE_CREDENTIALS_PATH = 'client_secret_xxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com.json' 13 | GOOGLE_SPREADSHEET_ID = '1bRQubfDVmFg53ohzY_SLSJRahf04kDtV2O0Ql28hP7U' 14 | GOOGLE_RANGE_NAME = 'foodmarkets' 15 | 16 | WOOSMAP_PRIVATE_API_KEY = 'eafb8805-3743-4cb9-abff-xxxxxxxxxxx' 17 | 18 | 19 | class GoogleSheets(object): 20 | """A wrapper around the Google Sheets API.""" 21 | 22 | API_NAME = 'sheets' 23 | API_VERSION = 'v4' 24 | DISCOVERY_URI = 'https://sheets.googleapis.com/$discovery/rest?version={apiVersion}' 25 | SCOPES = 'https://www.googleapis.com/auth/spreadsheets.readonly' 26 | APPLICATION_NAME = 'Google Sheets To Woosmap' 27 | REDIRECT_URI = 'http://localhost:8080' 28 | 29 | def __init__(self, credentials_path, spreadsheet_id, range_name=''): 30 | self.credentials_path = credentials_path 31 | self.spreadsheet_id = spreadsheet_id 32 | self.credentials = self.get_credentials() 33 | self.http = self.credentials.authorize(httplib2.Http()) 34 | if self.credentials is not None and self.credentials.access_token_expired: 35 | self.credentials.refresh(self.http) 36 | 37 | self.service = self.build_service() 38 | self.range_name = range_name if range_name else self.get_first_sheetname() 39 | 40 | def build_service(self): 41 | return discovery.build(self.API_NAME, self.API_VERSION, http=self.http, 42 | discoveryServiceUrl=self.DISCOVERY_URI) 43 | 44 | def get_credentials(self): 45 | storage_path = '.' + os.path.splitext(os.path.basename(__file__))[0] + '.credentials' 46 | storage = Storage(storage_path) 47 | credentials = storage.get() if os.path.exists(storage_path) else None 48 | if credentials is None or credentials.invalid: 49 | flow = flow_from_clientsecrets(self.credentials_path, self.SCOPES, redirect_uri=self.REDIRECT_URI) 50 | flow.user_agent = self.APPLICATION_NAME 51 | credentials = tools.run_flow(flow, storage) 52 | 53 | return credentials 54 | 55 | def get_values(self): 56 | return self.service.spreadsheets().values().get(spreadsheetId=self.spreadsheet_id, 57 | range=self.range_name).execute() 58 | 59 | def get_first_sheetname(self): 60 | sheet_metadata = self.service.spreadsheets().get(spreadsheetId=self.spreadsheet_id).execute() 61 | return sheet_metadata.get('sheets', '')[0].get("properties", {}).get("title", "Sheet1") 62 | 63 | 64 | class Woosmap: 65 | """A wrapper around the Woosmap Data API.""" 66 | 67 | WOOSMAP_API_HOSTNAME = 'api.woosmap.com' 68 | 69 | def __init__(self): 70 | self.session = requests.Session() 71 | 72 | def delete(self): 73 | self.session.delete('https://{hostname}/stores/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 74 | params={'private_key': WOOSMAP_PRIVATE_API_KEY}) 75 | 76 | def post(self, payload): 77 | return self.session.post('https://{hostname}/stores/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 78 | params={'private_key': WOOSMAP_PRIVATE_API_KEY}, 79 | json={'stores': payload}) 80 | 81 | def end(self): 82 | self.session.close() 83 | 84 | 85 | def get_name(asset): 86 | name = asset.get('Name', '') 87 | if name: 88 | return name 89 | else: 90 | raise ValueError('Unable to get the Name') 91 | 92 | 93 | def generate_id(asset): 94 | asset_id = sha1(get_name(asset).encode('utf-8')).hexdigest() 95 | return asset_id 96 | 97 | 98 | def get_contact(asset): 99 | return { 100 | 'website': asset.get('Website', ''), 101 | 'phone': asset.get('Contact Phone', ''), 102 | 'email': asset.get('Contact Email', '') 103 | } 104 | 105 | 106 | def get_geometry(asset): 107 | latitude = asset.get('Latitude', None) 108 | longitude = asset.get('Longitude', None) 109 | if latitude is not None and longitude is not None: 110 | return { 111 | 'lat': float(latitude), 112 | 'lng': float(longitude) 113 | } 114 | else: 115 | raise ValueError('Unable to get the location') 116 | 117 | 118 | def get_address(asset): 119 | return { 120 | 'lines': [asset.get('Address Line', '')], 121 | 'city': asset.get('City', ''), 122 | 'zipcode': asset.get('Zipcode', '') 123 | } 124 | 125 | 126 | def convert_to_woosmap(asset): 127 | converted_asset = {} 128 | try: 129 | converted_asset.update({ 130 | 'storeId': generate_id(asset), 131 | 'name': get_name(asset), 132 | 'address': get_address(asset), 133 | 'contact': get_contact(asset), 134 | 'location': get_geometry(asset) 135 | }) 136 | except ValueError as ve: 137 | print('ValueError Raised {0} for Asset {1}'.format(ve, json.dumps(asset, indent=2))) 138 | 139 | return converted_asset 140 | 141 | 142 | def import_assets(assets_data, woosmap_api_helper): 143 | try: 144 | print('Batch import {count} Assets...'.format(count=len(assets_data))) 145 | response = woosmap_api_helper.post(assets_data) 146 | if response.status_code >= 400: 147 | response.raise_for_status() 148 | 149 | except requests.exceptions.HTTPError as http_exception: 150 | if http_exception.response.status_code >= 400: 151 | print('Woosmap API Import Error: {0}'.format(http_exception.response.text)) 152 | else: 153 | print('Error requesting the API: {0}'.format(http_exception)) 154 | return False 155 | except Exception as exception: 156 | print('Failed importing Assets! {0}'.format(exception)) 157 | return False 158 | 159 | print('Successfully imported in {0} seconds'.format(response.elapsed.total_seconds())) 160 | return True 161 | 162 | 163 | def batch(assets_data, n=1): 164 | l = len(assets_data) 165 | for ndx in range(0, l, n): 166 | yield assets_data[ndx:min(ndx + n, l)] 167 | 168 | 169 | def main(): 170 | google_sheets = GoogleSheets(GOOGLE_CREDENTIALS_PATH, GOOGLE_SPREADSHEET_ID) 171 | 172 | sheet_data = google_sheets.get_values().get('values', []) 173 | header = sheet_data.pop(0) 174 | assets_as_dict = [dict(zip(header, item)) for item in sheet_data] 175 | 176 | woosmap_assets = [] 177 | for asset in assets_as_dict: 178 | converted_asset = convert_to_woosmap(asset) 179 | if bool(converted_asset): 180 | woosmap_assets.append(converted_asset) 181 | 182 | print('{0} Assets converted from source file'.format(len(woosmap_assets))) 183 | 184 | woosmap_api_helper = Woosmap() 185 | # /!\ deleting existing assets before posting new ones /!\ 186 | woosmap_api_helper.delete() 187 | 188 | count_imported_assets = 0 189 | for chunk in batch(woosmap_assets): 190 | imported_success = import_assets(chunk, woosmap_api_helper) 191 | if imported_success: 192 | count_imported_assets += len(chunk) 193 | 194 | woosmap_api_helper.end() 195 | 196 | 197 | if __name__ == '__main__': 198 | main() 199 | -------------------------------------------------------------------------------- /python-samples/woosmap_jsonschema_validation/foodmarkets.json: -------------------------------------------------------------------------------- 1 | { 2 | "stores": [ 3 | { 4 | "address": { 5 | "city": "Rotterdam", 6 | "lines": [ 7 | "Dominee Jan Scharpstraat 298" 8 | ], 9 | "zipcode": "3011 GZ" 10 | }, 11 | "storeId": "d3911140d1fbadb82b83e74486af99142623a0dc", 12 | "location": { 13 | "lat": 51.919948, 14 | "lng": 4.486843 15 | }, 16 | "name": "Markthal Rotterdam", 17 | "contact": { 18 | "website": "http://markthalrotterdam.nl/", 19 | "phone": "+31 (0)30 234 64 64", 20 | "email": "info@markthalrotterdam.nl" 21 | } 22 | }, 23 | { 24 | "address": { 25 | "city": "Roma", 26 | "lines": [ 27 | "Via Galvani/Via Alessandro Volta" 28 | ], 29 | "zipcode": "00118" 30 | }, 31 | "storeId": "6f4c75c5e74336d99ab73bd715ce1ce0a3850970", 32 | "location": { 33 | "lat": 41.877657, 34 | "lng": 12.473909 35 | }, 36 | "name": "Testaccio Market", 37 | "contact": { 38 | "website": "http://www.mercatotestaccio.com/", 39 | "phone": "+39 06 578 0638", 40 | "email": "info@mercatotestaccio.ocm" 41 | } 42 | }, 43 | { 44 | "address": { 45 | "city": "Vienna", 46 | "lines": [ 47 | "Via Galvani/Via Alessandro Volta" 48 | ], 49 | "zipcode": "1060" 50 | }, 51 | "storeId": "72adb1b1419c16f4d28f90d4ac4394de25f2f555", 52 | "location": { 53 | "lat": 48.199044, 54 | "lng": 16.364234 55 | }, 56 | "name": "Naschmarkt Vienna", 57 | "contact": { 58 | "website": "http://www.naschmarkt-vienna.com/", 59 | "phone": "+43 1 240555", 60 | "email": "contact@naschmarkt-vienna.com" 61 | } 62 | }, 63 | { 64 | "address": { 65 | "city": "Madrid", 66 | "lines": [ 67 | "Plaza de San Miguel" 68 | ], 69 | "zipcode": "28005" 70 | }, 71 | "storeId": "8af852d45a9059090b5225a96cf424ae0ddcefc5", 72 | "location": { 73 | "lat": 40.415261, 74 | "lng": -3.708944 75 | }, 76 | "name": "Mercado de San Miguel", 77 | "contact": { 78 | "website": "http://www.mercadodesanmiguel.es/en", 79 | "phone": "(+34) 915 42 49 36", 80 | "email": "administracion@mercadodesanmiguel.es" 81 | } 82 | }, 83 | { 84 | "address": { 85 | "city": "Barcelona", 86 | "lines": [ 87 | "La Rambla, 91" 88 | ], 89 | "zipcode": "08001" 90 | }, 91 | "storeId": "ba3599fcc23989456c146997e68b1a7816fcf5fc", 92 | "location": { 93 | "lat": 41.381635, 94 | "lng": 2.171596 95 | }, 96 | "name": "Mercado de La Boqueria", 97 | "contact": { 98 | "website": "http://www.boqueria.info/", 99 | "phone": "+34 933 18 25 84", 100 | "email": "administracion@mercadodesanmiguel.es" 101 | } 102 | }, 103 | { 104 | "address": { 105 | "city": "Nice", 106 | "lines": [ 107 | "Place Charles F\u00e9lix" 108 | ], 109 | "zipcode": "06300" 110 | }, 111 | "storeId": "e49e1fcf75bd3b509bf5b5201903ca5270930977", 112 | "location": { 113 | "lat": 43.69553, 114 | "lng": 7.275492 115 | }, 116 | "name": "Cours Saleya", 117 | "contact": { 118 | "website": "http://leblogduvieuxnice.nicematin.com/.services/blog/6a0120a864ed46970b0162fd89fc17970d/search?filter.q=march\u00e9", 119 | "phone": "", 120 | "email": "" 121 | } 122 | }, 123 | { 124 | "address": { 125 | "city": "Paris", 126 | "lines": [ 127 | "Place d\u2019Aligre" 128 | ], 129 | "zipcode": "75012" 130 | }, 131 | "storeId": "7fe6cfa7b39dbae2eec8fdd7faed315561619cff", 132 | "location": { 133 | "lat": 48.849021, 134 | "lng": 2.3777 135 | }, 136 | "name": "March\u00e9 d\u2019Aligre", 137 | "contact": { 138 | "website": "http://equipement.paris.fr/marche-couvert-beauvau-marche-d-aligre-5480", 139 | "phone": "01 45 11 71 11", 140 | "email": "" 141 | } 142 | }, 143 | { 144 | "address": { 145 | "city": "Firenze", 146 | "lines": [ 147 | "Piazza del Mercato Centrale" 148 | ], 149 | "zipcode": "50123" 150 | }, 151 | "storeId": "6080ed9f535dcb2e925c334068abe0a4431bb08d", 152 | "location": { 153 | "lat": 43.77654, 154 | "lng": 11.253133 155 | }, 156 | "name": "Mercato Centrale di San Lorenzo", 157 | "contact": { 158 | "website": "http://www.mercatocentrale.it", 159 | "phone": "+39 0552399798", 160 | "email": "info@mercatocentrale.it" 161 | } 162 | }, 163 | { 164 | "address": { 165 | "city": "Copenhagen", 166 | "lines": [ 167 | "Frederiksborggade 21" 168 | ], 169 | "zipcode": "1360" 170 | }, 171 | "storeId": "56eb5f571b8c083d384658a95a929ea997d69f9f", 172 | "location": { 173 | "lat": 55.684025, 174 | "lng": 12.569469 175 | }, 176 | "name": "Torvehallerne", 177 | "contact": { 178 | "website": "http://torvehallernekbh.dk", 179 | "phone": "+39 0552399798", 180 | "email": "info@torvehallernekbh.dk" 181 | } 182 | }, 183 | { 184 | "address": { 185 | "city": "London", 186 | "lines": [ 187 | "8 Southwark St" 188 | ], 189 | "zipcode": "SE1 1TL" 190 | }, 191 | "storeId": "74cf59f30119a3672647ef0c4bd9f0587cfcfba4", 192 | "location": { 193 | "lat": 51.505046, 194 | "lng": -0.090679 195 | }, 196 | "name": "Borough Market", 197 | "contact": { 198 | "website": "http://boroughmarket.org.uk", 199 | "phone": "020 7407 1002", 200 | "email": "" 201 | } 202 | }, 203 | { 204 | "address": { 205 | "city": "M\u00fcnchen", 206 | "lines": [ 207 | "Viktualienmarkt 3" 208 | ], 209 | "zipcode": "80881" 210 | }, 211 | "storeId": "71504c368f415a820795c254d852cadfd711651c", 212 | "location": { 213 | "lat": 48.135105, 214 | "lng": 11.576246 215 | }, 216 | "name": "Victuals Market", 217 | "contact": { 218 | "website": "http://www.muenchen.de/int/en/shopping/markets/viktualienmarkt.html", 219 | "phone": "+49 89 89068205", 220 | "email": "" 221 | } 222 | }, 223 | { 224 | "address": { 225 | "city": "S\u00e8te", 226 | "lines": [ 227 | "Rue Gambetta" 228 | ], 229 | "zipcode": "34200" 230 | }, 231 | "storeId": "e5bac7b59e5f6a2d4260b412904e09052f6df12c", 232 | "location": { 233 | "lat": 43.40214, 234 | "lng": 3.69545 235 | }, 236 | "name": "Halles de S\u00e8te", 237 | "contact": { 238 | "website": "http://www.halles-sete.com/", 239 | "phone": "04 99 04 70 00", 240 | "email": "commerce-artisanat@ville-sete.fr" 241 | } 242 | }, 243 | { 244 | "address": { 245 | "city": "Uz\u00e8s", 246 | "lines": [ 247 | "Place aux Herbes" 248 | ], 249 | "zipcode": "30700" 250 | }, 251 | "storeId": "e50bcf462a9fa23cedb6ef2fe57b81472f3477ba", 252 | "location": { 253 | "lat": 44.011821, 254 | "lng": 4.418889 255 | }, 256 | "name": "March\u00e9 d'Uz\u00e8s", 257 | "contact": { 258 | "website": "http://www.uzes.fr/Calendrier-des-marches-brocantes-et-foires_a126.html", 259 | "phone": "+33 (0)4 66 22 68 88", 260 | "email": "" 261 | } 262 | }, 263 | { 264 | "address": { 265 | "city": "Apt", 266 | "lines": [ 267 | "Place de la Bouquerie" 268 | ], 269 | "zipcode": "84400" 270 | }, 271 | "storeId": "8b685237005769cf1b85a6eb07ccc156b820ec9b", 272 | "location": { 273 | "lat": 43.876503, 274 | "lng": 5.393588 275 | }, 276 | "name": "Le Grand March\u00e9 d'Apt", 277 | "contact": { 278 | "website": "http://www.luberon-apt.fr/index.php/fr/sortir/les-marches", 279 | "phone": "+33 (0)4 90 74 03 18", 280 | "email": "oti@paysapt-luberon.fr" 281 | } 282 | }, 283 | { 284 | "address": { 285 | "city": "La Flotte", 286 | "lines": [ 287 | "Rue du March\u00e9" 288 | ], 289 | "zipcode": 17630 290 | }, 291 | "storeId": "846c55afb649f6c5715fd0d04700fbac8c369611", 292 | "location": { 293 | "lat": 46.187465, 294 | "lng": -1.327086 295 | }, 296 | "name": "Le March\u00e9 de la Flotte", 297 | "contact": { 298 | "website": "http://laflotte.fr/index.php/Vie-quotidienne/les-marches.html", 299 | "phone": "05.46.09.15.00", 300 | "email": "annie@laflotte.fr" 301 | } 302 | }, 303 | { 304 | "address": { 305 | "city": "Edimburgh", 306 | "lines": [ 307 | "Castle Terrace" 308 | ], 309 | "zipcode": "EH1 UK" 310 | }, 311 | "storeId": "01d822835de61e34be80dc747fe0e73773aef015", 312 | "location": { 313 | "lat": 55.947817, 314 | "lng": -3.203562 315 | }, 316 | "name": "Edimburgh Farmers' Market", 317 | "contact": { 318 | "website": "http://www.edinburghfarmersmarket.co.uk/", 319 | "phone": "0131 220 8580", 320 | "email": "" 321 | } 322 | }, 323 | { 324 | "address": { 325 | "city": "Belfast", 326 | "lines": [ 327 | "12 - 20 East Bridge Street" 328 | ], 329 | "zipcode": "BT1 3NQ" 330 | }, 331 | "storeId": "7fc2088804d1700be543f85eb44392134a1e3b4b", 332 | "location": { 333 | "lat": 54.596073, 334 | "lng": -5.921654 335 | }, 336 | "name": "St George's Market", 337 | "contact": { 338 | "website": "http://www.belfastcity.gov.uk/tourism-venues/stgeorgesmarket/stgeorgesmarket-index.aspx", 339 | "phone": "028 9043 5704", 340 | "email": "markets@belfastcity.gov.uk" 341 | } 342 | }, 343 | { 344 | "address": { 345 | "city": "Lille", 346 | "lines": [ 347 | "Place de la nouvelle aventure" 348 | ], 349 | "zipcode": "59000" 350 | }, 351 | "storeId": "b61c2fca4f713cb9b6136150a9459f5be59a5eed", 352 | "location": { 353 | "lat": 50.62671, 354 | "lng": 3.049325 355 | }, 356 | "name": "Halles de Wazemmes", 357 | "contact": { 358 | "website": "http://www.halles-wazemmes.com/", 359 | "phone": "", 360 | "email": "", 361 | "ErrorKey":2 362 | } 363 | 364 | } 365 | ] 366 | } -------------------------------------------------------------------------------- /python-samples/woosmap_jsonschema_validation/woosmap_jsonschema_validation.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | from jsonschema import ValidationError, validate 4 | 5 | JSON_TO_VALIDATE = os.path.join(os.getcwd(), 'foodmarkets.json') 6 | 7 | WOOSMAP_SCHEMA = { 8 | '$ref': 'https://raw.githubusercontent.com/woosmap/woosmap-json-schema/master/asset.json#' 9 | } 10 | WOOSMAP_COLLECTION_SCHEMA = { 11 | '$ref': 'https://raw.githubusercontent.com/woosmap/woosmap-json-schema/master/assetCollection.json#' 12 | } 13 | 14 | 15 | def load_json(file_path): 16 | with open(file_path, 'r') as f: 17 | return json.load(f) 18 | 19 | 20 | def validate_collection(assets): 21 | """ Validate an array of Assets expected as {"stores":[{asset},{asset},...]} 22 | Less time consuming but will raise ValidationError at first error """ 23 | try: 24 | print("Validating Collection of Assets...") 25 | validate(assets, WOOSMAP_COLLECTION_SCHEMA) 26 | except ValidationError as error: 27 | print Exception("Asset not Matching: {0}".format(error.message)) 28 | else: 29 | print("...Validated Collection Successful!") 30 | 31 | 32 | def validate_one_by_one(assets): 33 | """ Validate individually each Asset that could be useful to identify 34 | all Asset which could be in wrong schema. A little slower """ 35 | print("Validating assets individually..") 36 | for item in assets["stores"]: 37 | try: 38 | validate(item, WOOSMAP_SCHEMA) 39 | except ValidationError as error: 40 | print Exception( 41 | "Asset not Matching: {0}".format(error.message)) 42 | else: 43 | print("...Validated Asset {id} Successful!".format(id=item["storeId"])) 44 | 45 | 46 | def main(): 47 | assets_to_validate = load_json(JSON_TO_VALIDATE) 48 | validate_collection(assets_to_validate) 49 | validate_one_by_one(assets_to_validate) 50 | 51 | 52 | if __name__ == '__main__': 53 | if os.path.exists(os.path.join(os.getcwd(), JSON_TO_VALIDATE)): 54 | main() 55 | else: 56 | print('File not found: {0} '.format(JSON_TO_VALIDATE)) 57 | -------------------------------------------------------------------------------- /python-samples/woosmap_to_geojson/woosmap_to_geojson.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import json 3 | 4 | import requests 5 | 6 | origin_public_key = 'woos-3886aa76-xxxxxxxxxx' 7 | output_file = origin_public_key + '.json' 8 | allowed_referer = 'http://localhost/' 9 | api_server_host = 'api.woosmap.com' 10 | geojson_features = [] 11 | 12 | 13 | def get_all_stores(page=1): 14 | params = dict(key=origin_public_key, page=page) 15 | ref_header = {'referer': allowed_referer} 16 | 17 | response = session.get(url='http://{api_server_host}/stores'.format( 18 | api_server_host=api_server_host), 19 | params=params, 20 | headers=ref_header) 21 | 22 | temp_json = response.json() 23 | for feature in temp_json['features']: 24 | geojson_features.append(feature) 25 | 26 | if temp_json['pagination']['page'] != temp_json['pagination']['pageCount']: 27 | get_all_stores(temp_json['pagination']['page'] + 1) 28 | 29 | return geojson_features 30 | 31 | 32 | def export_input_geojson(inputjson): 33 | with codecs.open(output_file, 'w', encoding='utf8') as outfile: 34 | woosmap_geojson = {'type': 'FeatureCollection', 'features': inputjson} 35 | json.dump(woosmap_geojson, outfile, indent=2, ensure_ascii=False) 36 | 37 | 38 | if __name__ == '__main__': 39 | session = requests.Session() 40 | batch = [] 41 | try: 42 | stores_geojson = get_all_stores() 43 | export_input_geojson(stores_geojson) 44 | 45 | except BaseException as error: # bad bad way! 46 | print('An exception occurred: {}'.format(error)) 47 | -------------------------------------------------------------------------------- /python-samples/woosmap_to_woosmap/README.md: -------------------------------------------------------------------------------- 1 | # Python Script to export data from a Woosmap Customer as a JSON file and eventually import them to another customer. 2 | 3 | You need to define parameter for **public_key** (origin data). 4 | You could define parameter for **private key** (destination project) if you want to re-import them. 5 | 6 | sample usage: 7 | 8 | python woosmap_to_woosmap.py 9 | 10 | The exported data is saved in the file `data.json` -------------------------------------------------------------------------------- /python-samples/woosmap_to_woosmap/woosmap_to_woosmap.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | import codecs 4 | 5 | origin_public_key = 'woos-54e9fe79-5c35-3641-ace8-215e5610278d' 6 | private_key = '' 7 | output_file = 'woos-54e9fe79-5c35-3641-ace8-215e5610278d.json' 8 | allowed_referer = 'http://localhost/' 9 | api_server_host = 'api.woosmap.com' 10 | geojson_features = [] 11 | stores_batch_size = 500 12 | 13 | 14 | def get_geometry(store): 15 | return { 16 | 'lat': store['geometry']['coordinates'][1], 17 | 'lng': store['geometry']['coordinates'][0] 18 | } 19 | 20 | 21 | def transform_geojson_woosmap(extracted_geojson): 22 | stores = [] 23 | for feature in extracted_geojson: 24 | try: 25 | prop = feature["properties"] 26 | stores.append({"location": get_geometry(feature), 27 | "storeId": prop.get("store_id"), 28 | "openingHours": prop.get("opening_hours", {}), 29 | "userProperties": prop.get("user_properties", {}), 30 | "types": prop.get("types", []), 31 | "address": prop.get("address", {}), 32 | "name": prop.get("name", ""), 33 | "tags": prop.get("tags", []), 34 | "contact": prop.get("contact", {})}) 35 | except BaseException as error: 36 | print('An exception occurred: {}'.format(error)) 37 | 38 | return stores 39 | 40 | 41 | def get_all_stores(page=1): 42 | params = dict(key=origin_public_key, page=page) 43 | ref_header = {'referer': allowed_referer} 44 | 45 | response = session.get(url='http://{api_server_host}/stores'.format( 46 | api_server_host=api_server_host), 47 | params=params, 48 | headers=ref_header) 49 | 50 | temp_json = response.json() 51 | for feature in temp_json['features']: 52 | geojson_features.append(feature) 53 | 54 | if temp_json['pagination']['page'] != temp_json['pagination']['pageCount']: 55 | get_all_stores(temp_json['pagination']['page'] + 1) 56 | 57 | return geojson_features 58 | 59 | 60 | def export_input_json(inputjson): 61 | with codecs.open(output_file, 'w', encoding='utf8') as outfile: 62 | woosmap_data = {'stores': inputjson} 63 | json.dump(woosmap_data, outfile, indent=2, ensure_ascii=False) 64 | 65 | 66 | def import_location(locations): 67 | print('Importing locations (%d) ...' % len(locations)) 68 | response = session.post( 69 | 'http://{api_server_host}/stores'.format( 70 | api_server_host=api_server_host), 71 | params={'private_key': private_key}, 72 | json={'stores': locations}) 73 | 74 | print('Import time:', response.elapsed.total_seconds()) 75 | if response.status_code >= 400: 76 | print('Import Failed') 77 | print(response.text) 78 | return False 79 | 80 | return True 81 | 82 | 83 | if __name__ == '__main__': 84 | session = requests.Session() 85 | batch = [] 86 | try: 87 | stores_geojson = get_all_stores() 88 | stores_woosmap = transform_geojson_woosmap(stores_geojson) 89 | if output_file: 90 | export_input_json(stores_woosmap) 91 | if private_key: 92 | for store in stores_woosmap: 93 | if len(batch) == stores_batch_size: 94 | batch_result = import_location(batch) 95 | batch = [] 96 | else: 97 | batch.append(store) 98 | 99 | if batch: 100 | batch_result = import_location(batch) 101 | batch = [] 102 | 103 | except BaseException as error: # bad bad way! 104 | print('An exception occurred: {}'.format(error)) 105 | -------------------------------------------------------------------------------- /python-samples/woosmapjson_import/README.md: -------------------------------------------------------------------------------- 1 | # Python Script to import a simple dataset to Woosmap Database. 2 | 3 | sample usage: 4 | 5 | python woosmapjson_import.py 6 | 7 | This python script may only be used for small data (less than 1000 locations). 8 | 9 | The sample JSON File (foodmarkets.json) represent a list of some of the best Food Markets in Europe. -------------------------------------------------------------------------------- /python-samples/woosmapjson_import/foodmarkets.json: -------------------------------------------------------------------------------- 1 | { 2 | "stores": [ 3 | { 4 | "types": [ 5 | "covered" 6 | ], 7 | "location": { 8 | "lat": 51.919948, 9 | "lng": 4.486843 10 | }, 11 | "storeId": "markthalrotterdam", 12 | "name": "Markthal Rotterdam", 13 | "address": { 14 | "lines": [ 15 | "Dominee Jan Scharpstraat 298" 16 | ], 17 | "country": "Netherlands", 18 | "city": "Rotterdam", 19 | "zipcode": "3011 GZ" 20 | }, 21 | "contact": { 22 | "website": "http://markthalrotterdam.nl/", 23 | "phone": "+31 (0)30 234 64 64", 24 | "email": "info@markthalrotterdam.nl" 25 | }, 26 | "openingHours": { 27 | "timezone": "Europe/Amsterdam", 28 | "usual": { 29 | "default": [ 30 | { 31 | "start": "10:00", 32 | "end": "20:00" 33 | } 34 | ], 35 | "1": [] 36 | } 37 | } 38 | }, 39 | { 40 | "types": [ 41 | "covered" 42 | ], 43 | "location": { 44 | "lat": 41.877657, 45 | "lng": 12.473909 46 | }, 47 | "storeId": "testacciomarket", 48 | "name": "Testaccio Market", 49 | "address": { 50 | "lines": [ 51 | "Via Galvani/Via Alessandro Volta" 52 | ], 53 | "country": "Italy", 54 | "city": "Roma", 55 | "zipcode": "00118" 56 | }, 57 | "contact": { 58 | "website": "http://www.mercatotestaccio.com/", 59 | "phone": "+39 06 578 0638", 60 | "email": "info@mercatotestaccio.ocm" 61 | }, 62 | "openingHours": { 63 | "timezone": "Europe/Rome", 64 | "usual": { 65 | "default": [ 66 | { 67 | "all-day": true 68 | } 69 | ] 70 | } 71 | } 72 | }, 73 | { 74 | "types": [ 75 | "covered" 76 | ], 77 | "location": { 78 | "lat": 48.199044, 79 | "lng": 16.364234 80 | }, 81 | "storeId": "naschmarktvienna", 82 | "name": "Naschmarkt Vienna", 83 | "address": { 84 | "lines": [ 85 | "Via Galvani/Via Alessandro Volta" 86 | ], 87 | "country": "Austria", 88 | "city": "Vienna", 89 | "zipcode": "1060" 90 | }, 91 | "contact": { 92 | "website": "http://www.naschmarkt-vienna.com/", 93 | "tel": "+43 1 240555", 94 | "email": "contact@naschmarkt-vienna.com" 95 | }, 96 | "openingHours": { 97 | "timezone": "Europe/Vienna", 98 | "usual": { 99 | "default": [ 100 | { 101 | "start": "6:00", 102 | "end": "19:30" 103 | } 104 | ], 105 | "6": [ 106 | { 107 | "start": "6:00", 108 | "end": "17:00" 109 | } 110 | ], 111 | "7": [] 112 | } 113 | } 114 | }, 115 | { 116 | "types": [ 117 | "covered" 118 | ], 119 | "location": { 120 | "lat": 40.415261, 121 | "lng": -3.708944 122 | }, 123 | "storeId": "mercadodesanmiguel", 124 | "name": "Mercado de San Miguel", 125 | "address": { 126 | "lines": [ 127 | "Plaza de San Miguel" 128 | ], 129 | "country": "Spain", 130 | "city": "Madrid", 131 | "zipcode": "28005" 132 | }, 133 | "contact": { 134 | "website": "http://www.mercadodesanmiguel.es/en", 135 | "phone": "(+34) 915 42 49 36", 136 | "email": "administracion@mercadodesanmiguel.es" 137 | }, 138 | "openingHours": { 139 | "timezone": "Europe/Madrid", 140 | "usual": { 141 | "default": [ 142 | { 143 | "start": "10:00", 144 | "end": "23:59" 145 | } 146 | ], 147 | "4": [ 148 | { 149 | "start": "10:00", 150 | "end": "23:59" 151 | } 152 | ], 153 | "5": [ 154 | { 155 | "start": "10:00", 156 | "end": "23:59" 157 | } 158 | ], 159 | "6": [ 160 | { 161 | "start": "10:00", 162 | "end": "23:59" 163 | } 164 | ] 165 | } 166 | } 167 | }, 168 | { 169 | "types": [ 170 | "covered" 171 | ], 172 | "location": { 173 | "lat": 41.381635, 174 | "lng": 2.171596 175 | }, 176 | "storeId": "mercadodelaboqueria", 177 | "name": "Mercado de La Boqueria", 178 | "address": { 179 | "lines": [ 180 | "La Rambla, 91" 181 | ], 182 | "country": "Spain", 183 | "city": "Barcelona", 184 | "zipcode": "08001" 185 | }, 186 | "contact": { 187 | "website": "http://www.boqueria.info/", 188 | "phone": "+34 933 18 25 84", 189 | "email": "administracion@mercadodesanmiguel.es" 190 | }, 191 | "openingHours": { 192 | "timezone": "Europe/Madrid", 193 | "usual": { 194 | "default": [ 195 | { 196 | "start": "08:00", 197 | "end": "20:30" 198 | } 199 | ], 200 | "7": [] 201 | } 202 | } 203 | }, 204 | { 205 | "types": [ 206 | "covered" 207 | ], 208 | "location": { 209 | "lat": 43.695530, 210 | "lng": 7.275492 211 | }, 212 | "storeId": "courssaleya", 213 | "name": "Cours Saleya", 214 | "address": { 215 | "lines": [ 216 | "Place Charles Félix" 217 | ], 218 | "country": "France", 219 | "city": "Nice", 220 | "zipcode": "06300" 221 | }, 222 | "contact": { 223 | "website": "http://leblogduvieuxnice.nicematin.com/.services/blog/6a0120a864ed46970b0162fd89fc17970d/search?filter.q=marché" 224 | }, 225 | "openingHours": { 226 | "timezone": "Europe/Paris", 227 | "usual": { 228 | "default": [ 229 | { 230 | "start": "06:00", 231 | "end": "17:30" 232 | } 233 | ], 234 | "1": [], 235 | "7": [ 236 | { 237 | "start": "06:00", 238 | "end": "13:30" 239 | } 240 | ] 241 | } 242 | } 243 | }, 244 | { 245 | "types": [ 246 | "covered" 247 | ], 248 | "location": { 249 | "lat": 48.849021, 250 | "lng": 2.377700 251 | }, 252 | "storeId": "marchedaligre", 253 | "name": "Marché d’Aligre", 254 | "address": { 255 | "lines": [ 256 | "Place d’Aligre" 257 | ], 258 | "country": "France", 259 | "city": "Paris", 260 | "zipcode": "75012" 261 | }, 262 | "contact": { 263 | "website": "http://equipement.paris.fr/marche-couvert-beauvau-marche-d-aligre-5480", 264 | "phone": "01 45 11 71 11" 265 | }, 266 | "openingHours": { 267 | "timezone": "Europe/Paris", 268 | "usual": { 269 | "default": [ 270 | { 271 | "start": "09:00", 272 | "end": "13:00" 273 | }, 274 | { 275 | "start": "16:00", 276 | "end": "19:30" 277 | } 278 | ], 279 | "6": [ 280 | { 281 | "start": "09:00", 282 | "end": "13:00" 283 | }, 284 | { 285 | "start": "15:30", 286 | "end": "19:30" 287 | } 288 | ], 289 | "7": [ 290 | { 291 | "start": "09:00", 292 | "end": "13:00" 293 | } 294 | ] 295 | } 296 | } 297 | }, 298 | { 299 | "types": [ 300 | "covered" 301 | ], 302 | "location": { 303 | "lat": 43.776540, 304 | "lng": 11.253133 305 | }, 306 | "storeId": "mercatocentraledisanlorenzo", 307 | "name": "Mercato Centrale di San Lorenzo", 308 | "address": { 309 | "lines": [ 310 | "Piazza del Mercato Centrale", 311 | "Via dell'Ariento" 312 | ], 313 | "country": "Italy", 314 | "city": "Firenze", 315 | "zipcode": "50123" 316 | }, 317 | "contact": { 318 | "website": "http://www.mercatocentrale.it", 319 | "phone": "+39 0552399798", 320 | "email": "info@mercatocentrale.it" 321 | }, 322 | "openingHours": { 323 | "timezone": "Europe/Madrid", 324 | "usual": { 325 | "default": [ 326 | { 327 | "start": "10:00", 328 | "end": "23:59" 329 | } 330 | ] 331 | } 332 | } 333 | }, 334 | { 335 | "types": [ 336 | "covered" 337 | ], 338 | "location": { 339 | "lat": 55.684025, 340 | "lng": 12.569469 341 | }, 342 | "storeId": "torvehallerne", 343 | "name": "Torvehallerne", 344 | "address": { 345 | "lines": [ 346 | "Frederiksborggade 21" 347 | ], 348 | "country": "Denmark", 349 | "city": "Copenhagen", 350 | "zipcode": "1360" 351 | }, 352 | "contact": { 353 | "website": "http://torvehallernekbh.dk", 354 | "phone": "+39 0552399798", 355 | "email": "info@torvehallernekbh.dk" 356 | }, 357 | "openingHours": { 358 | "timezone": "Europe/Copenhagen", 359 | "usual": { 360 | "default": [ 361 | { 362 | "start": "10:00", 363 | "end": "19:00" 364 | } 365 | ], 366 | "5": [ 367 | { 368 | "start": "10:00", 369 | "end": "20:00" 370 | } 371 | ], 372 | "6": [ 373 | { 374 | "start": "10:00", 375 | "end": "18:00" 376 | } 377 | ], 378 | "7": [ 379 | { 380 | "start": "11:00", 381 | "end": "17:00" 382 | } 383 | ] 384 | } 385 | } 386 | }, 387 | { 388 | "types": [ 389 | "covered" 390 | ], 391 | "location": { 392 | "lat": 51.505046, 393 | "lng": -0.090679 394 | }, 395 | "storeId": "boroughmarket", 396 | "name": "Borough Market", 397 | "address": { 398 | "lines": [ 399 | "8 Southwark St" 400 | ], 401 | "country": "United Kingdom", 402 | "city": "London", 403 | "zipcode": "SE1 1TL" 404 | }, 405 | "contact": { 406 | "website": "http://boroughmarket.org.uk", 407 | "phone": "020 7407 1002" 408 | }, 409 | "openingHours": { 410 | "timezone": "Europe/London", 411 | "usual": { 412 | "default": [ 413 | { 414 | "start": "10:00", 415 | "end": "17:00" 416 | } 417 | ], 418 | "5": [ 419 | { 420 | "start": "10:00", 421 | "end": "18:00" 422 | } 423 | ], 424 | "7": [] 425 | } 426 | } 427 | }, 428 | { 429 | "types": [ 430 | "covered" 431 | ], 432 | "location": { 433 | "lat": 48.135105, 434 | "lng": 11.576246 435 | }, 436 | "storeId": "victualsmarket", 437 | "name": "Victuals Market", 438 | "address": { 439 | "lines": [ 440 | "Viktualienmarkt 3" 441 | ], 442 | "country": "Germany", 443 | "city": "München", 444 | "zipcode": "80881" 445 | }, 446 | "contact": { 447 | "website": "http://www.muenchen.de/int/en/shopping/markets/viktualienmarkt.html", 448 | "phone": "+49 89 89068205" 449 | }, 450 | "openingHours": { 451 | "timezone": "Europe/Berlin", 452 | "usual": { 453 | "default": [ 454 | { 455 | "start": "10:00", 456 | "end": "18:00" 457 | } 458 | ], 459 | "6": [ 460 | { 461 | "start": "10:00", 462 | "end": "15:00" 463 | } 464 | ], 465 | "7": [] 466 | } 467 | } 468 | }, 469 | { 470 | "types": [ 471 | "covered" 472 | ], 473 | "location": { 474 | "lat": 43.40214, 475 | "lng": 3.69545 476 | }, 477 | "storeId": "hallesdesete", 478 | "name": "Halles de Sète", 479 | "address": { 480 | "lines": [ 481 | "Rue Gambetta" 482 | ], 483 | "country": "France", 484 | "city": "Sète", 485 | "zipcode": "34200" 486 | }, 487 | "contact": { 488 | "website": "http://www.halles-sete.com/", 489 | "phone": "04 99 04 70 00", 490 | "email": "commerce-artisanat@ville-sete.fr" 491 | }, 492 | "openingHours": { 493 | "timezone": "Europe/Paris", 494 | "usual": { 495 | "default": [ 496 | { 497 | "start": "08:00", 498 | "end": "13:00" 499 | } 500 | ] 501 | } 502 | } 503 | }, 504 | { 505 | "types": [ 506 | "outside" 507 | ], 508 | "location": { 509 | "lat": 44.011821, 510 | "lng": 4.418889 511 | }, 512 | "storeId": "marcheduzes", 513 | "name": "Marché d'Uzès", 514 | "address": { 515 | "lines": [ 516 | "Place aux Herbes" 517 | ], 518 | "country": "France", 519 | "city": "Uzès", 520 | "zipcode": "30700" 521 | }, 522 | "contact": { 523 | "website": "http://www.uzes.fr/Calendrier-des-marches-brocantes-et-foires_a126.html", 524 | "phone": "+33 (0)4 66 22 68 88" 525 | }, 526 | "openingHours": { 527 | "timezone": "Europe/Paris", 528 | "usual": { 529 | "default": [ 530 | { 531 | "start": "07:30", 532 | "end": "13:00" 533 | } 534 | ], 535 | "1": [], 536 | "2": [], 537 | "4": [], 538 | "5": [], 539 | "7": [] 540 | } 541 | } 542 | }, 543 | { 544 | "types": [ 545 | "outside" 546 | ], 547 | "location": { 548 | "lat": 43.876503, 549 | "lng": 5.393588 550 | }, 551 | "storeId": "marchedapt", 552 | "name": "Le Grand Marché d'Apt", 553 | "address": { 554 | "lines": [ 555 | "Place de la Bouquerie" 556 | ], 557 | "country": "France", 558 | "city": "Apt", 559 | "zipcode": "84400" 560 | }, 561 | "contact": { 562 | "website": "http://www.luberon-apt.fr/index.php/fr/sortir/les-marches", 563 | "phone": "+33 (0)4 90 74 03 18", 564 | "email": "oti@paysapt-luberon.fr" 565 | }, 566 | "openingHours": { 567 | "timezone": "Europe/Paris", 568 | "usual": { 569 | "default": [ 570 | { 571 | "start": "08:00", 572 | "end": "13:00" 573 | } 574 | ], 575 | "1": [], 576 | "2": [], 577 | "3": [], 578 | "4": [], 579 | "5": [], 580 | "7": [] 581 | } 582 | } 583 | }, 584 | { 585 | "types": [ 586 | "outside" 587 | ], 588 | "location": { 589 | "lat": 46.187465, 590 | "lng": -1.327086 591 | }, 592 | "storeId": "marchedelaflotte", 593 | "name": "Le Marché de la Flotte", 594 | "address": { 595 | "lines": [ 596 | "Rue du Marché" 597 | ], 598 | "country": "France", 599 | "city": "La Flotte", 600 | "zipcode": "17630" 601 | }, 602 | "contact": { 603 | "website": "http://laflotte.fr/index.php/Vie-quotidienne/les-marches.html", 604 | "phone": "05.46.09.15.00", 605 | "email": "annie@laflotte.fr" 606 | }, 607 | "openingHours": { 608 | "timezone": "Europe/Paris", 609 | "usual": { 610 | "default": [ 611 | { 612 | "start": "08:00", 613 | "end": "13:00" 614 | } 615 | ] 616 | } 617 | } 618 | }, 619 | { 620 | "types": [ 621 | "outside" 622 | ], 623 | "location": { 624 | "lat": 55.947817, 625 | "lng": -3.203562 626 | }, 627 | "storeId": "edimburghfarmersmarket", 628 | "name": "Edimburgh Farmers' Market", 629 | "address": { 630 | "lines": [ 631 | "Castle Terrace" 632 | ], 633 | "country": "Scotland", 634 | "city": "Edimburgh", 635 | "zipcode": "EH1 UK" 636 | }, 637 | "contact": { 638 | "website": "http://www.edinburghfarmersmarket.co.uk/", 639 | "phone": "0131 220 8580" 640 | }, 641 | "openingHours": { 642 | "timezone": "Europe/London", 643 | "usual": { 644 | "default": [], 645 | "6": [ 646 | { 647 | "start": "09:00", 648 | "end": "14:00" 649 | } 650 | ] 651 | } 652 | } 653 | }, 654 | { 655 | "types": [ 656 | "covered" 657 | ], 658 | "location": { 659 | "lat": 54.596073, 660 | "lng": -5.921654 661 | }, 662 | "storeId": "stgeorgesmarket", 663 | "name": "St George's Market", 664 | "address": { 665 | "lines": [ 666 | "12 - 20 East Bridge Street" 667 | ], 668 | "country": "Ireland", 669 | "city": "Belfast", 670 | "zipcode": "BT1 3NQ" 671 | }, 672 | "contact": { 673 | "website": "http://www.belfastcity.gov.uk/tourism-venues/stgeorgesmarket/stgeorgesmarket-index.aspx", 674 | "phone": "028 9043 5704", 675 | "email": "markets@belfastcity.gov.uk" 676 | }, 677 | "openingHours": { 678 | "timezone": "Europe/London", 679 | "usual": { 680 | "default": [], 681 | "5": [ 682 | { 683 | "start": "06:00", 684 | "end": "14:00" 685 | } 686 | ], 687 | "6": [ 688 | { 689 | "start": "09:00", 690 | "end": "15:00" 691 | } 692 | ], 693 | "7": [ 694 | { 695 | "start": "10:00", 696 | "end": "16:00" 697 | } 698 | ] 699 | } 700 | } 701 | }, 702 | { 703 | "types": [ 704 | "covered" 705 | ], 706 | "location": { 707 | "lat": 50.626710, 708 | "lng": 3.049325 709 | }, 710 | "storeId": "hallesdewazemmes", 711 | "name": "Halles de Wazemmes", 712 | "address": { 713 | "lines": [ 714 | "Place de la nouvelle aventure" 715 | ], 716 | "country": "France", 717 | "city": "Lille", 718 | "zipcode": "59000" 719 | }, 720 | "contact": { 721 | "website": "http://www.halles-wazemmes.com/" 722 | }, 723 | "openingHours": { 724 | "timezone": "Europe/Paris", 725 | "usual": { 726 | "default": [ 727 | { 728 | "start": "08:00", 729 | "end": "14:00" 730 | } 731 | ], 732 | "1":[], 733 | "5": [ 734 | { 735 | "start": "08:00", 736 | "end": "20:00" 737 | } 738 | ], 739 | "6": [ 740 | { 741 | "start": "08:00", 742 | "end": "20:00" 743 | } 744 | ], 745 | "7": [ 746 | { 747 | "start": "08:00", 748 | "end": "15:00" 749 | } 750 | ] 751 | } 752 | } 753 | } 754 | ] 755 | } -------------------------------------------------------------------------------- /python-samples/woosmapjson_import/woosmapjson_import.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | 4 | WOOSMAP_JSON_FILE = 'foodmarkets.json' 5 | WOOSMAP_PRIVATE_API_KEY = '23713926-1af5-4321-ba54-xxxxxxxxxxx' 6 | 7 | 8 | class Woosmap: 9 | """A wrapper around the Woosmap Data API.""" 10 | 11 | WOOSMAP_API_HOSTNAME = 'api.woosmap.com' 12 | 13 | def __init__(self): 14 | self.session = requests.Session() 15 | 16 | def delete(self): 17 | self.session.delete('https://{hostname}/stores/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 18 | params={'private_key': WOOSMAP_PRIVATE_API_KEY}) 19 | 20 | def post(self, payload): 21 | return self.session.post('https://{hostname}/stores/'.format(hostname=self.WOOSMAP_API_HOSTNAME), 22 | params={'private_key': WOOSMAP_PRIVATE_API_KEY}, 23 | json={'stores': payload}) 24 | 25 | def end(self): 26 | self.session.close() 27 | 28 | 29 | def import_assets(assets_data, woosmap_api_helper): 30 | try: 31 | print('Batch import {count} Assets...'.format(count=len(assets_data))) 32 | response = woosmap_api_helper.post(assets_data) 33 | if response.status_code >= 400: 34 | response.raise_for_status() 35 | 36 | except requests.exceptions.HTTPError as http_exception: 37 | if http_exception.response.status_code >= 400: 38 | print('Woosmap API Import Error: {0}'.format(http_exception.response.text)) 39 | else: 40 | print('Error requesting the API: {0}'.format(http_exception)) 41 | return False 42 | except Exception as exception: 43 | print('Failed importing Assets! {0}'.format(exception)) 44 | return False 45 | 46 | print('Successfully imported in {0} seconds'.format(response.elapsed.total_seconds())) 47 | return True 48 | 49 | 50 | def main(): 51 | with open(WOOSMAP_JSON_FILE, 'rb') as f: 52 | assets = json.loads(f.read()) 53 | try: 54 | woosmap_api_helper = Woosmap() 55 | # /!\ deleting existing assets before posting new ones /!\ 56 | woosmap_api_helper.delete() 57 | import_assets(assets["stores"], woosmap_api_helper) 58 | woosmap_api_helper.end() 59 | except Exception as error: 60 | print("Unable to import file {0} : {1}".format(WOOSMAP_JSON_FILE, error)) 61 | 62 | 63 | if __name__ == '__main__': 64 | main() 65 | --------------------------------------------------------------------------------