Browse Source

Create POI API and importer script

master
Skylar Ittner 2 years ago
commit
6b5a24152e
  1. 4
      .gitignore
  2. 16
      composer.json
  3. 122
      composer.lock
  4. BIN
      database.mwb
  5. 90
      import_csv.php
  6. 138
      index.php
  7. 7
      nbproject/project.properties
  8. 9
      nbproject/project.xml
  9. 167
      poi-types.csv
  10. 80
      required.php
  11. 36
      settings.template.php

4
.gitignore

@ -0,0 +1,4 @@
/settings.php
/database.mwb.bak
/vendor/
/nbproject/private

16
composer.json

@ -0,0 +1,16 @@
{
"name": "netsyms/points-of-interest-api",
"description": "Simple PHP API for getting OpenStreetMap points of interest as GeoJSON",
"type": "project",
"require": {
"catfan/medoo": "^1.6",
"anthonymartin/geo-location": "^1.0"
},
"license": "MIT",
"authors": [
{
"name": "Skylar Ittner",
"email": "admin@netsyms.com"
}
]
}

122
composer.lock

@ -0,0 +1,122 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "7717ebd7c56c3d078f88c2c9aab56f61",
"packages": [
{
"name": "anthonymartin/geo-location",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/anthonymartin/GeoLocation.php.git",
"reference": "50bf026f069296dfae11aa195d987854b2e75855"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/anthonymartin/GeoLocation.php/zipball/50bf026f069296dfae11aa195d987854b2e75855",
"reference": "50bf026f069296dfae11aa195d987854b2e75855",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "class",
"autoload": {
"psr-0": {
"AnthonyMartin": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"CC 3.0"
],
"authors": [
{
"name": "Anthony Martin",
"email": "anthony@replaycreative.com",
"homepage": "http://replaycreative.com",
"role": "Developer"
}
],
"description": "Retrieve bounding coordinates, distances, longitude and latitude with GeoLocation.class.php",
"homepage": "https://github.com/anthonymartin/GeoLocation.php",
"keywords": [
"bounding coordinates",
"distances",
"geocoding",
"geolocation"
],
"time": "2016-09-17T18:05:14+00:00"
},
{
"name": "catfan/medoo",
"version": "v1.6.1",
"source": {
"type": "git",
"url": "https://github.com/catfan/Medoo.git",
"reference": "53a02b300d673f716cb06bf0e24fd774ec53939f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/catfan/Medoo/zipball/53a02b300d673f716cb06bf0e24fd774ec53939f",
"reference": "53a02b300d673f716cb06bf0e24fd774ec53939f",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"php": ">=5.4"
},
"suggest": {
"ext-pdo_dblib": "For MSSQL or Sybase database on Linux/UNIX platform",
"ext-pdo_mysql": "For MySQL or MariaDB database",
"ext-pdo_oci": "For Oracle database",
"ext-pdo_oci8": "For Oracle version 8 database",
"ext-pdo_pqsql": "For PostgreSQL database",
"ext-pdo_sqlite": "For SQLite database",
"ext-pdo_sqlsrv": "For MSSQL database"
},
"type": "framework",
"autoload": {
"psr-4": {
"Medoo\\": "/src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Angel Lai",
"email": "angel@catfan.me"
}
],
"description": "The lightest PHP database framework to accelerate development",
"homepage": "https://medoo.in",
"keywords": [
"database",
"lightweight",
"mariadb",
"mssql",
"mysql",
"oracle",
"php framework",
"postgresql",
"sql",
"sqlite"
],
"time": "2018-12-08T20:24:23+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

BIN
database.mwb

90
import_csv.php

@ -0,0 +1,90 @@
<?php
/*
* Import a CSV file generated by osmpois.jar
* https://github.com/MorbZ/OsmPoisPbf
*
* Expected CSV format:
65|N357939737|48.5841285|-114.9976581|Swamp Mountain
65|N357939740|47.7252186|-113.6412010|Swan Glaciers
65|N357939743|47.7193851|-113.6414787|Swan Peak
37|N357939746|47.7688309|-113.8928792|Swan River State Forest
21|N357939752|47.4904920|-113.6795331|Swan Valley School
65|N357939758|48.4088516|-114.7782011|Mount Swaney
65|N357939762|48.3680110|-115.4601645|Swede Mountain
65|N357939764|46.6479788|-114.2214988|Sweeney Peak
65|N357939778|45.1641103|-109.6046133|Sylvan Peak
*/
require __DIR__ . '/settings.php';
if (!$SETTINGS['debug']) {
error_reporting(0);
} else {
error_reporting(E_ALL);
ini_set('display_errors', 'On');
}
//
// Composer
require __DIR__ . '/vendor/autoload.php';
date_default_timezone_set($SETTINGS['timezone']);
use Medoo\Medoo;
$database;
try {
$database = new Medoo([
'database_type' => $SETTINGS['database']['type'],
'database_name' => $SETTINGS['database']['name'],
'server' => $SETTINGS['database']['server'],
'username' => $SETTINGS['database']['user'],
'password' => $SETTINGS['database']['password'],
'charset' => $SETTINGS['database']['charset']
]);
} catch (Exception $ex) {
die("Database error: $ex\n");
}
if (!file_exists($argv[1])) {
die("Please supply the filename of the CSV file to import.\n");
}
$current = 0;
$handle = fopen($argv[1], "r");
if ($handle) {
$batchinsert = [];
while (($line = fgets($handle)) !== false) {
$fields = explode("|", trim($line), 5);
$batchinsert[] = [
"osmid" => substr($fields[1], 1),
"typeid" => $fields[0],
"latitude" => $fields[2],
"longitude" => $fields[3],
"eletype" => $fields[1][0],
"name" => $fields[4]
];
$current++;
if ($current % 100 == 0) {
$database->insert("poi", $batchinsert);
$error = $database->error();
if ($error[1] != 0 && $error[0] != "23000") {
die("\nDatabase error $error[0]: $error[2]\n");
}
$batchinsert = [];
echo " $current ";
}
}
fclose($handle);
echo "\nDone: $current records processed.\n";
} else {
die("Error: could not open the file.\n");
}

138
index.php

@ -0,0 +1,138 @@
<?php
require_once __DIR__ . "/required.php";
function getgeojson($data) {
global $SETTINGS;
$geojson = [
"status" => "OK"
];
// If the data is an array, make a FeatureCollection
// If there's no results, make an empty FeatureCollection
if (count($data) == 0 || is_array($data[0])) {
$geojson["type"] = "FeatureCollection";
$geojson["results"] = count($data);
$geojson["max_results"] = $SETTINGS["max_results"];
$geojson["features"] = [];
foreach ($data as $item) {
$geojson["features"][] = [
"type" => "Feature",
"geometry" => [
"type" => "Point",
"coordinates" => [
$item["longitude"] * 1.0,
$item["latitude"] * 1.0
]
],
"properties" => [
"osmid" => $item["osmid"] * 1,
"name" => $item["name"],
"typeid" => $item["typeid"] * 1,
"type" => $item["type"],
"eletype" => $item["eletype"]
]
];
}
} else {
$geojson["type"] = "Feature";
$geojson["geometry"] = [
"type" => "Point",
"coordinates" => [
$data["longitude"] * 1.0,
$data["latitude"] * 1.0
]
];
$geojson["properties"] = [
"osmid" => $data["osmid"] * 1,
"name" => $data["name"],
"typeid" => $data["typeid"] * 1,
"type" => $data["type"],
"eletype" => $data["eletype"]
];
}
return $geojson;
}
$where = [];
$validations = [
"osmid" => "/[0-9]+/",
"typeid" => "/[1-9]+/",
"type" => "/[A-Z]+_[A-Z]+",
"latitude" => "/[0-9]{0,3}\.[0-9]{2,10}/",
"longitude" => "/[0-9]{0,3}\.[0-9]{2,10}/",
"radius_km" => "/[0-9]+/",
"radius_mi" => "/[0-9]+/"
];
foreach ($validations as $name => $regex) {
if (!empty($_GET[$name])) {
if (is_array($_GET[$name])) {
foreach ($_GET[$name] as $t) {
if (!preg_match($regex, $t)) {
exit("[]");
}
}
} else {
if (!preg_match($regex, $_GET[$name])) {
exit("[]");
}
}
if (preg_match("/(osmid|typeid|type)/", $name)) {
$where[$name] = $_GET[$name];
}
}
}
// Calculate bounding box
use AnthonyMartin\GeoLocation\GeoLocation as GeoLocation;
if (!empty($_GET["latitude"]) && !empty($_GET["longitude"])) {
$userlocation = GeoLocation::fromDegrees($_GET["latitude"], $_GET["longitude"]);
$radius = $SETTINGS["default_radius"];
if (!empty($_GET["radius_km"])) {
$radius = $_GET["radius_km"] * 1.0;
} else if (!empty($_GET["radius_mi"])) {
$radius = $_GET["radius_mi"] / 0.62137;
}
$searchbounds = $userlocation->boundingCoordinates($radius, "kilometers");
$where["latitude[<>]"] = [$searchbounds[0]->getLatitudeInDegrees(), $searchbounds[1]->getLatitudeInDegrees()];
$where["longitude[<>]"] = [$searchbounds[0]->getLongitudeInDegrees(), $searchbounds[1]->getLongitudeInDegrees()];
}
// Remove database ambiguity
foreach ($where as $key => $value) {
if ($key == "typeid") {
$where["poi.typeid"] = $value;
unset($where["typeid"]);
}
}
if (count($where) > 1) {
$where = ["AND" => $where];
}
$where["LIMIT"] = $SETTINGS["max_results"];
ob_flush();
$database->debug()->select("poi", ["[>]types" => "typeid"], ["osmid", "poi.typeid", "type", "latitude", "longitude", "eletype", "name"], $where);
$query = ob_get_contents();
ob_clean();
$results = $database->query($query)->fetchAll();
$geojson = getgeojson($results);
if ($SETTINGS["debug"]) {
$geojson["query"] = $query;
}
if (empty($_GET["pretty"])) {
exit(json_encode($geojson));
}
exit(json_encode($geojson, JSON_PRETTY_PRINT));

7
nbproject/project.properties

@ -0,0 +1,7 @@
include.path=${php.global.include.path}
php.version=PHP_72
source.encoding=UTF-8
src.dir=.
tags.asp=false
tags.short=false
web.root=.

9
nbproject/project.xml

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.php.project</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/php-project/1">
<name>PointsOfInterestAPI</name>
</data>
</configuration>
</project>

167
poi-types.csv

@ -0,0 +1,167 @@
1,ACCOMMO_ALPINEHUT
2,ACCOMMO_CAMPING
3,ACCOMMO_CARAVAN
4,ACCOMMO_CHALET
5,ACCOMMO_HOSTEL
6,ACCOMMO_HOTEL
7,ACCOMMO_MOTEL
8,AMENITY_COURT
9,AMENITY_FIRESTATION
11,AMENITY_LIBRARY
12,AMENITY_PLAYGROUND
13,AMENITY_POLICE
14,AMENITY_POSTOFFICE
15,AMENITY_PRISON
16,AMENITY_PUBLICBUILDING
17,AMENITY_TOWNHALL
18,BARRIER_BLOCKS
19,EDUCATION_COLLEGE
20,EDUCATION_NURSERY
21,EDUCATION_SCHOOL
22,EDUCATION_UNIVERSITY
23,FOOD_BAR
24,FOOD_BIERGARTEN
25,FOOD_CAFE
26,FOOD_FASTFOOD
27,FOOD_ICECREAM
28,FOOD_PUB
29,FOOD_RESTAURANT
30,HEALTH_DENTIST
31,HEALTH_DOCTORS
32,HEALTH_HOSPITALEMERGENCY
33,HEALTH_HOSPITAL
34,HEALTH_PHARMACY
35,HEALTH_VETERINARY
36,LANDUSE_ALLOTMENTS
37,LANDUSE_CONIFEROUSDECIDUOUS
38,LANDUSE_CONIFEROUS
39,LANDUSE_DECIDUOUS
40,LANDUSE_GRASS
41,LANDUSE_HILLS
42,LANDUSE_MILITARY
43,LANDUSE_QUARY
44,LANDUSE_SCRUB
45,LANDUSE_SWAMP
46,MONEY_BANK
47,MONEY_EXCHANGE
48,POW_BAHAI
49,POW_BUDDHIST
50,POW_CHRISTIAN
51,POW_HINDU
52,POW_ISLAMIC
53,POW_JAIN
54,POW_JEWISH
55,POW_SHINTO
56,POW_SIKH
58,POW_UNKOWN
59,POI_CAVE
60,POI_CRANE
61,POI_EMBASSY
62,POI_BUNKER
63,POI_MINE
64,POI_PEAK1
65,POI_PEAK
66,POI_CITY
67,POI_HAMLET
68,POI_SUBURB
69,POI_TOWN
70,POI_VILLAGE
71,POI_TOWERCOMMUNICATION
72,POI_TOWERLOOKOUT
73,SHOP_ALCOHOL
74,SHOP_BAKERY
75,SHOP_BICYCLE
76,SHOP_BOOK
77,SHOP_BUTCHER
78,SHOP_CARREPAIR
79,SHOP_CAR
80,SHOP_CLOTHES
81,SHOP_COMPUTER
82,SHOP_CONFECTIONERY
83,SHOP_CONVENIENCE
84,SHOP_COPYSHOP
85,SHOP_DEPARTMENTSTORE
86,SHOP_DIY
87,SHOP_FISH
88,SHOP_FLORIST
89,SHOP_GARDENCENTRE
90,SHOP_GIFT
91,SHOP_GREENGROCER
92,SHOP_HAIRDRESSER
93,SHOP_HEARINGAIDS
94,SHOP_HIFI
95,SHOP_JEWELRY
96,SHOP_KIOSK
97,SHOP_LAUNDRETTE
98,SHOP_MARKETPLACE
99,SHOP_PHONE
100,SHOP_MOTORCYCLE
101,SHOP_MUSIC
102,SHOP_NEWSPAPER
103,SHOP_PET
104,SHOP_SHOES
105,SHOP_SUPERMARKET
106,SHOP_TOBACCO
107,SHOP_TOYS
108,SHOP_VENDINGMASCHINE
109,SHOP_VIDEORENTAL
110,SPORT_ARCHERY
111,SPORT_BASEBALL
112,SPORT_BASKETBALL
113,SPORT_BOWLING
114,SPORT_CANOE
115,SPORT_CRICKET
116,SPORT_DIVING
117,SPORT_FOOTBALL
118,SPORT_GOLF
119,SPORT_GYM
120,SPORT_GYMNASIUM
121,SPORT_CLIMBING
122,SPORT_HORSE
123,SPORT_ICESKATING
124,SPORT_LEISURECENTER
125,SPORT_MINIATURGOLF
126,SPORT_MOTORRACING
127,SPORT_SHOOTING
128,SPORT_SKATING
129,SPORT_SKIINGDOWNHILL
130,SPORT_SNOOKER
131,SPORT_SOCCER
132,SPORT_STADIUM
133,SPORT_SWIMMING
134,SPORT_TENNIS
135,SPORT_WATERSKI
136,SPORT_SURFING
137,TOURIST_ARCHAELOGICAL
138,TOURIST_ART
139,TOURIST_ATTRACTION
140,TOURIST_BATTLEFIELD
141,TOURIST_BEACH
142,TOURIST_CASTLE
143,TOURIST_CASTLE2
144,TOURIST_CINEMA
145,TOURIST_FOUNTAIN
146,TOURIST_INFORMATION
147,TOURIST_MEMORIAL
148,TOURIST_MONUMENT
149,TOURIST_MUSEUM
150,TOURIST_NIGHTCLUB
151,TOURIST_RUINS
152,TOURIST_THEATRE
153,TOURIST_THEMEPARK
156,TOURIST_WINDMILL
157,TOURIST_WRECK
158,TOURIST_ZOO
159,TRANSPORT_TERMINAL
160,TRANSPORT_AIRPORT
161,TRANSPORT_BUSSTOP
162,TRANSPORT_FUEL
163,TRANSPORT_LIGHTHOUSE
164,TRANSPORT_MARINA
165,TRANSPORT_RENTALCAR
166,TRANSPORT_SUBWAY
167,TRANSPORT_STATION
168,TRANSPORT_TRAMSTOP
169,WATER_DAM
170,WATER_TOWER
171,WATER_WEIR

80
required.php

@ -0,0 +1,80 @@
<?php
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This file contains global settings and utility functions.
*/
ob_start(); // allow sending headers after content
// Settings file
require __DIR__ . '/settings.php';
// Unicode, solves almost all stupid encoding problems
header('Content-Type: application/json; charset=utf-8');
//
// Composer
require __DIR__ . '/vendor/autoload.php';
date_default_timezone_set($SETTINGS['timezone']);
function sendError($msg) {
header('HTTP/1.1 500 Internal Server Error');
exit(json_encode(["status" => "ERROR", "message" => $msg]));
}
// Database settings
// Also inits database and stuff
use Medoo\Medoo;
$database;
try {
$database = new Medoo([
'database_type' => $SETTINGS['database']['type'],
'database_name' => $SETTINGS['database']['name'],
'server' => $SETTINGS['database']['server'],
'username' => $SETTINGS['database']['user'],
'password' => $SETTINGS['database']['password'],
'charset' => $SETTINGS['database']['charset']
]);
} catch (Exception $ex) {
sendError("Database error. Try again later. $ex");
}
if (!$SETTINGS['debug']) {
error_reporting(0);
} else {
error_reporting(E_ALL);
ini_set('display_errors', 'On');
}
$VARS;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$VARS = $_POST;
define("GET", false);
} else {
$VARS = $_GET;
define("GET", true);
}
/**
* Check if the previous database action had a problem.
* @param array $specials int=>string array with special response messages for SQL errors
*/
function checkDBError($specials = []) {
global $database;
$errors = $database->error();
if (!is_null($errors[1])) {
foreach ($specials as $code => $text) {
if ($errors[1] == $code) {
sendError($text);
}
}
sendError("A database error occurred: " . $errors[2]);
}
}

36
settings.template.php

@ -0,0 +1,36 @@
<?php
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// Settings for the app.
// Copy to settings.php and customize.
$SETTINGS = [
// Whether to output debugging info like PHP notices, warnings,
// and stacktraces.
// Turning this on in production is a security risk and can sometimes break
// things, such as JSON output where extra content is not expected.
"debug" => false,
// Database connection settings
// See http://medoo.in/api/new for info
"database" => [
"type" => "mysql",
"name" => "poidb",
"server" => "localhost",
"user" => "root",
"password" => "",
"charset" => "utf8"
],
// Maximum number of results to return.
"max_results" => 1000,
// Default radius when searching by location, in kilometers
"default_radius" => 10,
// Name of the app.
"site_title" => "OSM Points of Interest",
// For supported values, see http://php.net/manual/en/timezones.php
"timezone" => "America/Denver"
];
Loading…
Cancel
Save