{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": { "datalore": { "hide_input_from_viewers": false, "hide_output_from_viewers": false, "node_id": "Otlif4aaL90WNLAYUl0aZZ", "report_properties": { "relativeY": 0, "rowId": "Ata2kR9ues3l7HJzdQ618Y" }, "type": "MD" } }, "source": [ "# Pushkin as a Traveller\n", "\n", "Pushkin grew up in Moscow and St. Petersburg. In 1819 he was exiled, at the age of nineteen, to a small town called Kishinev and then he was moved to his family estate in Ekaterinoslav. He remained in exile for nearly seven years until 1826 when Tsar Nicholas came to power. Below you can see a map of his travels throughout Russia.\n", "\n", "\"Portrait\n", "\n", "[Original version](https://www.rgo.ru/ru/article/pushkin-puteshestvennik-linii-zhizni-linii-na-karte) of the graph that you can see below.\n", "\n", "Also, see the [short journey through his biography](https://pushkinland.ru/2018/english/push1.php).\n", "\n", "Some data was collected from other sources, such as:\n", "\n", "- [https://en.wikipedia.org/wiki/Alexander_Pushkin](https://en.wikipedia.org/wiki/Alexander_Pushkin)\n", "- [http://hronika.su/pushkin-a-s](http://hronika.su/pushkin-a-s)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "datalore": { "hide_input_from_viewers": false, "hide_output_from_viewers": false, "node_id": "bvDq3MEt7eSWfXx24Fo4eH", "report_properties": { "relativeY": 0, "rowId": "S5sRUICvTwXqvBa5n6zqDN" }, "type": "CODE" } }, "outputs": [ { "data": { "text/html": [ "
\n", " " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ " " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", " " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%useLatestDescriptors\n", "%use dataframe\n", "%use lets-plot\n", "%use lets-plot-gt(gt=\"[30,)\")" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "datalore": { "hide_input_from_viewers": false, "hide_output_from_viewers": false, "node_id": "ymaCgWMwCAnAAyXWLYq4P9", "report_properties": { "relativeY": 0, "rowId": "rBuLMv8TxpOXnq2znZoHGw" }, "type": "CODE" } }, "outputs": [], "source": [ "@file:DependsOn(\"org.geotools:gt-shapefile:[30,)\")\n", "@file:DependsOn(\"org.geotools:gt-cql:[30,)\")" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "datalore": { "hide_input_from_viewers": false, "hide_output_from_viewers": false, "node_id": "jjArluKzOm4PrQompahHE4", "report_properties": { "relativeY": 0, "rowId": "WWbwqYgJSbP3e2XvzYM6a0" }, "type": "CODE" } }, "outputs": [], "source": [ "import org.geotools.data.shapefile.ShapefileDataStoreFactory\n", "import org.geotools.data.simple.SimpleFeatureCollection\n", "import java.net.URL\n", "\n", "import org.geotools.filter.text.cql2.CQL" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "datalore": { "hide_input_from_viewers": false, "hide_output_from_viewers": false, "node_id": "qbTVpWuEkg6HlVGBBnJk9f", "report_properties": { "relativeY": 0, "rowId": "ambjtrRzyYQY5SWPpdqXbi" }, "type": "CODE" } }, "outputs": [], "source": [ "val factory = ShapefileDataStoreFactory()\n", "\n", "val worldFeatures : SimpleFeatureCollection = with(\"naturalearth_lowres\") {\n", " val url = \"https://raw.githubusercontent.com/JetBrains/lets-plot-kotlin/master/docs/examples/shp/${this}/${this}.shp\"\n", " factory.createDataStore(URL(url)).featureSource.features\n", "}\n", "val currentZone = worldFeatures.subCollection(CQL.toFilter(\"continent = 'Europe' OR continent = 'Asia' or continent = 'Africa'\"))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "datalore": { "hide_input_from_viewers": false, "hide_output_from_viewers": false, "node_id": "1IEoGjEijqDK0C9lUaxZ1o", "report_properties": { "relativeY": 0, "rowId": "mNC0NNxv4wOMviP0Hop4OR" }, "type": "CODE" } }, "outputs": [], "source": [ "fun appendStayTime(placesDf: DataFrame, movesDf: DataFrame): DataFrame {\n", " fun cityNameToYears(name: String): String {\n", " val departureYears: List = movesDf.sortBy(\"year\").filter { it[\"departure\"] == name }[\"year\"].toList() as List\n", " val rawArrivalYears: List = movesDf.sortBy(\"year\").filter { it[\"arrival\"] == name }[\"year\"].toList() as List\n", " val arrivalYears: List = if (!departureYears.isEmpty() && departureYears.min() < rawArrivalYears.min()) {\n", " listOf(departureYears.min()) + rawArrivalYears\n", " } else {\n", " rawArrivalYears\n", " }\n", " val allYearRanges: List> = arrivalYears.map { arrivalYear ->\n", " val departureYear = if (departureYears.isEmpty()) {\n", " arrivalYear\n", " } else {\n", " departureYears.firstOrNull { it >= arrivalYear } ?: arrivalYear\n", " }\n", " Pair(arrivalYear, departureYear)\n", " }.sortedByDescending { it.second - it.first }\n", " val yearRanges: MutableList> = mutableListOf()\n", " for (yearRange in allYearRanges) {\n", " val outerRange = yearRanges.firstOrNull { bigYearRange: Pair -> bigYearRange.first <= yearRange.first && yearRange.second <= bigYearRange.second }\n", " if (outerRange == null) yearRanges.add(yearRange)\n", " }\n", " return yearRanges.sortedBy { it.first }.map { yearRange ->\n", " if (yearRange.first == yearRange.second) {\n", " yearRange.first.toString()\n", " } else {\n", " \"${yearRange.first}-${yearRange.second}\"\n", " }\n", " }.joinToString(\", \")\n", " }\n", " return placesDf.add(\"years\") { cityNameToYears(it[\"name\"] as String) }\n", "}" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "datalore": { "hide_input_from_viewers": false, "hide_output_from_viewers": false, "node_id": "tfH9eoRr7ZyVfPH1iaqGr5", "report_properties": { "relativeY": 0, "rowId": "nFbgfR1ye5vZ4mz4pAV0N8" }, "type": "CODE" } }, "outputs": [], "source": [ "val citySize = 3\n", "val bigCitySize = 6\n", "val bigCities = listOf(\"Moscow\", \"Petersburg\")\n", "\n", "val rawPlacesDf = DataFrame.readCSV(\"https://raw.githubusercontent.com/JetBrains/lets-plot-docs/master/data/pushkin/places.csv\")\n", "\n", "val rawMovesDf = DataFrame.readCSV(\"https://raw.githubusercontent.com/JetBrains/lets-plot-docs/master/data/pushkin/moves.csv\")\n", "\n", "val movesDf = rawMovesDf.join(rawPlacesDf) { \"departure\" match \"name\" }\n", " .rename(\"longitude\" to \"from_lon\").rename(\"latitude\" to \"from_lat\")\n", " .join(rawPlacesDf) { \"arrival\" match \"name\" }\n", " .rename(\"longitude\" to \"to_lon\").rename(\"latitude\" to \"to_lat\")\n", " .add(\"size_start\") { if (it[\"departure\"] in bigCities) { bigCitySize } else { citySize } }\n", " .add(\"size_end\") { if (it[\"arrival\"] in bigCities) { bigCitySize } else { citySize } }\n", "\n", "val placesDf = appendStayTime(rawPlacesDf, rawMovesDf)\n", " .add(\"size\") { if (it[\"name\"] in bigCities) { bigCitySize } else { citySize } }\n", "\n", "val moscowDf = rawPlacesDf.filter { it[\"name\"] in bigCities }\n", "val labelsDf1 = rawPlacesDf.filter { it[\"name\"] in listOf(\"Nizhny Novgorod\", \"Novocherkassk\", \"Orenburg\", \"Stavropol\", \"Tiflis\", \"Vladikavkaz\") }\n", "val labelsDf2 = rawPlacesDf.filter { it[\"name\"] in listOf(\"Chișinău\", \"Erzurum\", \"Kiev\", \"Pskov\", \"Simferopol\", \"Uralsk\", \"Vitebsk\", \"Oryol\", \"Ekaterinoslav\") }" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "datalore": { "hide_input_from_viewers": false, "hide_output_from_viewers": false, "node_id": "pegsjsH5yrDQAz2gj7QCub", "report_properties": { "relativeY": 0, "rowId": "5ueTxlcoggZxwyv3W6CQdF" }, "type": "CODE" } }, "outputs": [ { "data": { "text/html": [ "
\n", " " ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val xmin = 24.0\n", "val xmax = 64.0\n", "val ymin = 38.0\n", "val ymax = 62.0\n", "\n", "letsPlot() +\n", " geomRect(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, size = 0, fill = \"#eef8ff\") +\n", " geomMap(data = currentZone.toSpatialDataset(), size = 0.1, color = \"#636363\", fill = \"#fcfffc\") +\n", " geomCurve(data = movesDf.toMap(), curvature = -.15, size = .75,\n", " arrow = arrow(type = \"closed\", length = 10, angle = 14))\n", " { x = \"from_lon\"; y = \"from_lat\"; xend = \"to_lon\"; yend = \"to_lat\"; color = \"path\";\n", " sizeStart = \"size_start\"; sizeEnd = \"size_end\" } + // It is necessary for the arrows not to overlap the points\n", " geomPoint(data = placesDf.toMap(), shape = 21, fill = \"white\",\n", " tooltips = layerTooltips().title(\"@name\").line(\"Visited in @years\"))\n", " { x = \"longitude\"; y = \"latitude\"; size = \"size\" } +\n", " geomText(data = moscowDf.toMap(), size = 8, fontface = \"bold\", hjust = 0, nudgeY = 0.75)\n", " { x = \"longitude\"; y = \"latitude\"; label = \"name\" } +\n", " geomText(data = labelsDf1.toMap(), size = 6, hjust = 0, nudgeX = 0.25, nudgeY = 0.5)\n", " { x = \"longitude\"; y = \"latitude\"; label = \"name\" } +\n", " geomText(data = labelsDf2.toMap(), size = 6, hjust = 1, nudgeX = -0.35, nudgeY = -0.1)\n", " { x = \"longitude\"; y = \"latitude\"; label = \"name\" } +\n", " scaleColorManual(values = listOf(\"#addd8e\", \"#e34a33\", \"#8856a7\", \"#2c7fb8\",\n", " \"#1c9099\", \"#006d2c\", \"#fec44f\", \"#636363\")) +\n", " scaleSizeIdentity(guide = \"none\") +\n", " coordMap(xlim = xmin to xmax, ylim = ymin to ymax) +\n", " ggsize(800, 800) +\n", " ggtitle(\"Alexander Pushkin's Trips\") +\n", " themeVoid() +\n", " theme(plotTitle = elementText(size = 20, margin = listOf(20, 0, 0, 0)), legendTitle = elementBlank())\n", " .legendPosition(1, 1)\n", " .legendJustification(1, 1)" ] } ], "metadata": { "datalore": { "base_environment": "default", "computation_mode": "JUPYTER", "package_manager": "pip", "packages": [], "report_row_ids": [ "Ata2kR9ues3l7HJzdQ618Y", "S5sRUICvTwXqvBa5n6zqDN", "rBuLMv8TxpOXnq2znZoHGw", "WWbwqYgJSbP3e2XvzYM6a0", "ambjtrRzyYQY5SWPpdqXbi", "mNC0NNxv4wOMviP0Hop4OR", "nFbgfR1ye5vZ4mz4pAV0N8", "5ueTxlcoggZxwyv3W6CQdF" ], "version": 3 }, "kernelspec": { "display_name": "Kotlin", "language": "kotlin", "name": "kotlin" }, "language_info": { "codemirror_mode": "text/x-kotlin", "file_extension": ".kt", "mimetype": "text/x-kotlin", "name": "kotlin", "nbconvert_exporter": "", "pygments_lexer": "kotlin", "version": "1.9.10" } }, "nbformat": 4, "nbformat_minor": 4 }